Canny edge detector

The most widely used edge detector turns the image gradient into thin, connected edge curves in four steps. Change the scale and the thresholds, and follow a single pixel through every step.

Image II click any view to select a pixel
1 · IxI_x derivative of Gaussian in x
1 · IyI_y derivative of Gaussian in y
2 · Magnitude ∥∇I∥\|\nabla I\| edge strength
2 · Orientation θ\theta hue, brightness from magnitude
3 · Non-maximum suppression thinned to one pixel
4 · Thresholds strong weak kept dropped
4 · Edges after hysteresis

What makes a good edge detector

Canny modeled edges as ideal steps corrupted by additive Gaussian noise and looked for the linear filter that optimizes the product of the signal-to-noise ratio and the localization. The first derivative of a Gaussian approximates this optimal operator closely. Filtering alone still gives thick, broken responses, so three more steps turn them into thin, connected curves.

Step 1: derivatives of Gaussian

The image is filtered with the xx and yy derivatives of a Gaussian, which smooths and differentiates at the same time, as explained in Derivatives & noise:

Ix=I∗∂Gσ∂x,Iy=I∗∂Gσ∂yI_x = I * \frac{\partial G_\sigma}{\partial x}, \qquad I_y = I * \frac{\partial G_\sigma}{\partial y}

IxI_x responds to vertical edges, IyI_y to horizontal ones. Positive values (bright) mean the intensity increases to the right or downwards.

Step 2: magnitude and orientation

Together, the two derivatives form the gradient ∇I=(Ix,Iy)\nabla I = (I_x, I_y). It points in the direction of the steepest increase in intensity, across the edge, and its length measures the edge strength:

∥∇I∥=Ix2+Iy2,θ=atan2⁡(Iy,Ix)\|\nabla I\| = \sqrt{I_x^2 + I_y^2}, \qquad \theta = \operatorname{atan2}(I_y, I_x)

Because yy points down in images, θ\theta turns clockwise on the screen. The two sides of a thin line have opposite gradients, so they show up in opposite colors in the orientation view.

Step 3: non-maximum suppression

The magnitude forms ridges that are several pixels wide, and wider for larger σ\sigma. To get one point per edge, a pixel qq is kept only if its magnitude is a maximum along the gradient direction. It is compared with the points one pixel ahead and one pixel behind,

p=q+∇I∥∇I∥,r=q−∇I∥∇I∥,p = q + \frac{\nabla I}{\|\nabla I\|}, \qquad r = q - \frac{\nabla I}{\|\nabla I\|},

which usually fall between pixel centers, so their magnitudes are interpolated from the four surrounding pixels. The pixel survives if ∥∇I∥(q)>∥∇I∥(p)\|\nabla I\|(q) > \|\nabla I\|(p) and ∥∇I∥(q)≥∥∇I∥(r)\|\nabla I\|(q) \ge \|\nabla I\|(r); the asymmetric comparison keeps exactly one pixel of a ridge with two equal values. Select a pixel and zoom in to see pp and rr drawn on the magnitude views.

Step 4: hysteresis thresholding

The thinned magnitude still contains small responses from noise and texture. A single threshold either lets them through or breaks real edges into dashes wherever their contrast dips. Hysteresis uses two thresholds:

Starting from the strong pixels, edges are followed into connected weak pixels, which is a connected components search over the 8 neighbors of each pixel. A curve has to be strong in one place only, and it is then kept as long as it stays above the low threshold. Here both thresholds are given as fractions of the largest magnitude in the image.

Choosing σ

The choice of σ\sigma depends on the desired behavior: a large σ\sigma detects large-scale edges and suppresses noise, a small σ\sigma detects fine features. Canny finds every intensity edge at the chosen scale, including texture and shadow boundaries. Which of them are useful is left to the application, which can only choose if all edges were found in the first place.

Try this