Filters and edge detection
Blur to remove noise, compute gradients, and choose Canny thresholds that hold up outside the demo image.
Smoothing before anything else
Edge detectors and thresholding amplify noise, so almost every classical pipeline starts with a blur. The kernel choice matters: a Gaussian is isotropic and fast, a median removes salt-and-pepper noise without smearing edges, and a bilateral filter smooths flat regions while keeping boundaries sharp.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gauss = cv2.GaussianBlur(gray, (5, 5), sigmaX=1.4) # kernel size must be odd
median = cv2.medianBlur(gray, 5) # great against speckle
bilateral = cv2.bilateralFilter(gray, 9, 75, 75) # slow, but preserves edges
print(gauss.shape, bilateral.shape) # same spatial size as input| Filter | Removes | Cost | Use when |
|---|---|---|---|
| GaussianBlur | General sensor noise | Low | The default before Canny or thresholding |
| medianBlur | Salt-and-pepper outliers | Low-medium | Dead pixels, harsh binary noise |
| bilateralFilter | Noise while keeping edges | High | Pre-processing before segmentation or stylisation |
| boxFilter / blur | Noise, uniformly | Lowest | Quick smoothing where quality is not critical |
Gradients and Canny
# Magnitude of the gradient: an edge is a place where brightness changes fast
gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3)
gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=3)
magnitude = cv2.magnitude(gx, gy) # float - uint8 would saturate at 255
# Canny keeps strong edges, then keeps weak ones only if linked to a strong one
edges = cv2.Canny(gauss, threshold1=80, threshold2=160)
print(edges.shape, edges.dtype, edges.max()) # single channel, uint8, 255 at an edge- Use a float depth (
CV_32F) for Sobel. Withuint8negative values wrap around and the result is meaningless. - In Canny,
threshold2controls how strong an edge must be to start a chain andthreshold1how weak an edge may be to continue one. Keep the ratio near 2:1 and move both together. - Pick thresholds from the image, not from a tutorial: the median of pixel intensity times 0.66 and 1.33 is a reasonable starting rule.
- Blur first and expect to re-tune. A threshold that works on a bright indoor photo will miss edges in an underexposed one.
⚠️
Canny thresholds are not model parameters you can copy between datasets. A pipeline tuned on one camera, exposure or lighting condition will silently degrade when any of those change, so validate on a sample of real frames and log the thresholds you shipped with.
FAQ
Why does Canny return almost nothing on my image?
Usually noise has already been removed too aggressively by a blur, or the thresholds are too high for a low-contrast image. Inspect the gradient magnitude to see which of the two it is.
Do I need a blur before Sobel?
Yes in practice. Raw gradients respond to noise as strongly as to structure, and smoothing first makes the gradient map actually reflect edges in the scene.
Related
Reading, writing and inspecting images Contours and object detection basics
Last refreshed 2026-09-18.