Recipe
Run the Canny edge detector
Use when: you need thin, well-placed, reliably found edges from a grayscale image, not just a raw gradient-magnitude threshold
Goal: a binary edge map: one pixel wide per genuine edge, noise rejected, faint-but-real edge segments kept
Taught in The full Canny pipeline, step by step
Quick recipe
-
1
Convolve the image with a Gaussian kernel (Chapter 4). Choose sigma to balance suppressing noise against losing genuine fine edges.
Example
A noisy 1\times 5 row [40,90,45,205,190] smoothed lightly becomes about [55,58,113,165,197]. The sharp mid-row noise spike at 90\to45 calms down. The real dark-to-bright change around index 2-4 survives.
Why: taking a derivative of a noisy signal directly makes the noise worse. A derivative reacts to small, fast wiggles just as strongly as it reacts to real, large-scale changes. Smoothing first keeps the noise from taking over the next step.
-
2
Apply Sobel or Prewitt (Sections 2-3) to get Gx and Gy. Combine them into magnitude M=\sqrt{G_x^2+G_y^2} and direction \theta=\operatorname{atan2}(G_y,G_x) at every pixel.
Example
At one pixel, G_x=90, G_y=120. Magnitude: \sqrt{90^2+120^2}=\sqrt{8100+14400}=\sqrt{22500}=150. Direction: \operatorname{atan2}(120,90)\approx 53.13°.
Why: magnitude and direction are exactly the two numbers Steps 3 and 4 need. Magnitude gets compared against thresholds. Direction says which neighbors to check for the local-maximum test.
-
3
At every pixel, compare its magnitude to its two neighbors along the gradient direction (Section 7). Keep it only if it is greater than or equal to both. Otherwise, zero it out.
Example
A pixel with magnitude 150 has direction-aligned neighbors reading 90 and 60. Since 150\ge 90 and 150\ge 60, it survives. A neighboring pixel with magnitude 90, whose own direction-aligned neighbor is this 150-magnitude pixel, does not survive, since 90<150.
Why: this step turns a multi-pixel-wide gradient response into the single-pixel-wide ridge Canny's "single response" goal needs. Skip it, and the next step gets a wide band to sort, instead of a clean line.
-
4
Classify every surviving pixel. Magnitude at or above t_{\text{high}} is a strong edge, accepted right away. Magnitude between t_{\text{low}} and t_{\text{high}} is weak, accepted only if connected to a strong pixel. Magnitude below t_{\text{low}} is thrown out.
Example
With t_{\text{low}}=30, t_{\text{high}}=100: a pixel at 150 is strong, accepted outright. A pixel at 50 connected to that strong pixel is accepted as a linked weak edge. A different, lone pixel at 50 with no strong neighbor anywhere in its chain is thrown out.
Why: a single threshold cannot tell a genuinely faint edge segment apart from noise of similar size. The connectivity rule uses the surrounding structure that a lone cutoff cannot see.
-
5
Trace every accepted strong pixel and its chain of accepted weak pixels into one connected group. The union of all such groups is the final binary edge map.
Example
Strong pixels at positions (2,2) and (2,7), linked through a chain of accepted weak pixels at (2,3) through (2,6), become one single connected edge segment. It spans (2,2) through (2,7) in the output.
Why: this step produces the actual deliverable, a binary image, not a scattered set of accept-or-reject calls. That image is ready for whatever comes next (segmentation, feature matching) that needs a clean edge map as input.
Common mistakes
- Skipping Step 1's smoothing to save computing time. That lets noise corrupt the gradient in Step 2 and spoil the final result.
- Applying hysteresis thresholding straight to the raw gradient magnitude, skipping non-maximum suppression. This sorts a whole wide band of pixels per edge, instead of a thin ridge, defeating Canny's single-response goal.
- Setting t_{\text{low}} too close to t_{\text{high}}. This makes hysteresis behave almost like a single threshold, losing its edge-linking benefit entirely.
- Forgetting that gradient direction points across an edge, not along it. This leads to checking the wrong pair of neighbors during non-maximum suppression (Step 3).