Edge Detection

Run Sobel, Prewitt and Laplacian kernels on a real image, step through every stage of the Canny detector, and check the arithmetic at any pixel.

Intermediate

A 160×120 grayscale test image generated in the browser. Tap any image to inspect a pixel.

Original (grayscale)
Gx (horizontal change)
Gy (vertical change)
Magnitude |G|
Direction θ (hue)

Gx and Gy: orange = brightness increases (right / down), blue = it decreases. Direction: hue encodes the angle of the gradient (red → right, yellow-green → down, cyan → left, violet → up), brightness encodes strength.

Pixel inspector at (71, 45)

The 3×3 neighbourhood of the original image around the tapped pixel, and the exact arithmetic the kernel performs there.

Neighbourhood

229
90
96
228
97
95
231
90
90

Gx kernel

-1
0
1
-2
0
2
-1
0
1
×

pixel × weight

-229
0
96
-456
0
190
-231
0
90
→

sum

Gx = -540

(-229) + 96 + (-456) + 190 + (-231) + 90 = -540

Gy kernel

-1
-2
-1
0
0
0
1
2
1
×

pixel × weight

-229
-180
-96
0
0
0
231
180
90
→

sum

Gy = -4

(-229) + (-180) + (-96) + 231 + 180 + 90 = -4

|G| = √(Gx² + Gy²) = √(-540² + -4²) = 540.0

θ = atan2(Gy, Gx) = -179.6°

What edge detection does

An edge is a place where image brightness changes quickly. Edge detectors estimate the derivative of the image with small kernels: a 3×3 grid of weights slid over every pixel. Sobel and Prewitt estimate the first derivative in x and y separately, giving a gradient vector with a strength (magnitude) and a direction. The Laplacian estimates the second derivative, which is zero right at the middle of an edge.

Every image on this page is computed for real in your browser from the pixel values: nothing is pre-rendered. The only simplification is the size (160×120 pixels) so the pipeline reruns instantly when you move a slider.

An edge is a fast change

Where brightness jumps from dark to light, the first derivative spikes and the second derivative crosses zero.

Gradients have a direction

Gx and Gy together form a vector that points from dark to light. The edge itself runs perpendicular to it.

Derivatives amplify noise

Subtracting neighbouring pixels turns small random noise into many small "edges", so detectors smooth first.

Canny adds decisions

Thinning (non-max suppression) and two thresholds turn a blurry gradient image into clean one-pixel contours.

Key formulas

Gradient magnitude and direction

|G| = √(Gx² + Gy²), θ = atan2(Gy, Gx)

Gx, Gy
responses of the horizontal and vertical kernels at a pixel
|G|
edge strength
θ
direction of fastest brightness increase (edge runs at θ + 90°)

Applying a 3×3 kernel

G(x, y) = Σi,j ∈ {-1,0,1} K(i, j) · I(x + i, y + j)

K
the kernel weights
I
image brightness

Gaussian blur used by Canny

g(x) = e−x² / 2σ² / Σ e−k² / 2σ²

σ
standard deviation: larger σ smooths more and keeps only bigger edges

The Canny pipeline

  1. Blur with a Gaussian of width σ to suppress noise.
  2. Gradient: Sobel Gx and Gy, then magnitude and direction.
  3. Non-maximum suppression: round the direction to 0°, 45°, 90° or 135° and keep a pixel only if its magnitude is at least as large as both neighbours along that direction.
  4. Double threshold: above high is strong, between low and high is weak, below low is dropped.
  5. Hysteresis: keep weak pixels only if they are connected (8-neighbourhood) to a strong pixel.
DetectorKernelMeasuresNoise handling
Sobel[-1 0 1; -2 0 2; -1 0 1]First derivative, with built-in smoothing across the edgeModerate
Prewitt[-1 0 1; -1 0 1; -1 0 1]First derivative, plain average across the edgeSlightly worse than Sobel
Laplacian[0 1 0; 1 -4 1; 0 1 0]Second derivative, all directions at oncePoor: amplifies noise
CannyGaussian + Sobel + NMS + hysteresisThin, connected edge mapGood, tuned by σ

How to use this visualizer

  1. 1
    Start in Gradient operators mode and switch between Sobel, Prewitt and Laplacian. Compare Gx, Gy, magnitude and the direction (hue) image.
  2. 2
    Raise the noise slider and watch the Laplacian fill with speckle much sooner than Sobel.
  3. 3
    Tap any image to move the inspector. It shows the 3×3 neighbourhood, each kernel weight times its pixel, and the sum.
  4. 4
    Switch to Canny pipeline. Step through the five stages with the buttons or thumbnails.
  5. 5
    Increase σ to remove noise edges (and fine detail). Move the low and high thresholds and watch which weak pixels survive hysteresis.
  6. 6
    Use your own image with the upload button. It is converted to grayscale locally and never uploaded.

Code

JavaScript
// Sobel gradient at pixel (x, y) of a grayscale image (values 0..255)
const KX = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
const KY = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];

function sobelAt(img, x, y) {
  let gx = 0, gy = 0;
  for (let j = -1; j <= 1; j++) {
    for (let i = -1; i <= 1; i++) {
      const v = img.get(x + i, y + j); // clamp at the borders
      gx += KX[j + 1][i + 1] * v;
      gy += KY[j + 1][i + 1] * v;
    }
  }
  return { magnitude: Math.hypot(gx, gy), angle: Math.atan2(gy, gx) };
}

Practice problems

Problem 1: Sobel by hand

A 3×3 neighbourhood is [10 10 10; 10 10 10; 90 90 90] (rows top to bottom). Compute Gx, Gy, |G| and θ with the Sobel kernels.
Show solution

Gx: every row is constant, so (−1·10 + 1·10) etc. sums to 0. Gx = 0.

Gy = −1·10 −2·10 −1·10 + 1·90 + 2·90 + 1·90 = −40 + 360 = 320.

|G| = √(0² + 320²) = 320.

θ = atan2(320, 0) = 90°: brightness increases downward.

A horizontal edge, strength 320, gradient pointing down.

Problem 2: Which pixels survive?

After non-max suppression, a row of pixels has magnitudes 30, 70, 140, 60, 0, all 8-connected in a line. With low = 50 and high = 120, which pixels are in the final Canny edge map?
Show solution

Classify: 30 → dropped, 70 → weak, 140 → strong, 60 → weak, 0 → dropped.

Hysteresis starts at the strong pixel (140) and grows into connected weak neighbours: 70 and 60.

Final edge: the pixels with 70, 140 and 60.

If the 140 were 110 instead, there would be no strong pixel and the whole run would be dropped.

Discussion

Questions, ideas or a different approach? Join the conversation.