Computer Vision Chapter 6

Edge detection

Edges are places where intensity changes quickly. We estimate that change with derivatives (gradients) of the image, then thin and link strong responses into clean contours. This chapter builds from Sobel/Scharr and Laplacian to the full Canny pipeline—with several ready-to-run OpenCV examples.

Gradients on a grid

For a 2D image I(x, y), the gradient ∇I = (∂I/∂x, ∂I/∂y) points in the direction of steepest brightness increase. On a discrete grid we approximate partial derivatives with finite differences—exactly what convolution kernels like Sobel implement. The gradient magnitude |∇I| ≈ √(Gx² + Gy²) highlights edges regardless of orientation; the angle atan2(Gy, Gx) describes edge direction.

Gx, Gy

Horizontal and vertical derivative images. Large values where intensity jumps across columns or rows.

Magnitude

Combines both directions—useful for visualization and non-max suppression in Canny.

Pre-blur

Derivatives amplify noise. A little Gaussian blur before Sobel/Canny stabilizes results.

Sobel and Scharr

cv2.Sobel computes separable 3×3 (or larger, odd) derivative filters. Use CV_64F (float) to avoid clipping negative slopes, then rescale for display. Scharr is a 3×3 variant with slightly better rotation invariance for equal cost.

import cv2
import numpy as np

gray = cv2.imread("photo.jpg", cv2.IMREAD_GRAYSCALE)
blur = cv2.GaussianBlur(gray, (5, 5), 0)

gx = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3)
gy = cv2.Sobel(blur, cv2.CV_64F, 0, 1, ksize=3)
mag = np.sqrt(gx * gx + gy * gy)
mag_u8 = np.uint8(255 * mag / (mag.max() + 1e-6))

gx_s = cv2.Scharr(blur, cv2.CV_64F, 1, 0)
gy_s = cv2.Scharr(blur, cv2.CV_64F, 0, 1)

Convert to uint8 only for saving or imshow; keep floats for further math.

Gradient angle (optional)

angle = np.arctan2(gy, gx)  # radians, in [-pi, pi]

Laplacian

The Laplacian is the second derivative (sum of ∂²/∂x² and ∂²/∂y²). It is sensitive to noise but highlights fine detail and zero-crossings near edges. Often used in sharpening (unsharp mask) and as a building block in blob detection.

import cv2

gray = cv2.imread("photo.jpg", cv2.IMREAD_GRAYSCALE)
lap = cv2.Laplacian(gray, cv2.CV_64F, ksize=3)
lap_vis = cv2.convertScaleAbs(lap)

convertScaleAbs maps floats to 8-bit for display after Laplacian/Sobel.

Canny edge detector

Canny is a multi-stage algorithm: Gaussian smoothing, gradient magnitude and angle, non-maximum suppression (thin edges along the gradient normal), and hysteresis thresholding with a low and high threshold. Pixels above threshold2 are strong edges; weak edges are kept only if connected to strong ones—this reduces broken fragments.

import cv2

gray = cv2.imread("photo.jpg", cv2.IMREAD_GRAYSCALE)
blur = cv2.GaussianBlur(gray, (5, 5), 0)

# Classic ratio: t_high : t_low often ~ 2:1 or 3:1; tune per image scale
t1, t2 = 50, 150
edges = cv2.Canny(blur, t1, t2, apertureSize=3, L2gradient=False)

More Canny examples

# Finer edges (noisier): lower thresholds
fine = cv2.Canny(blur, 30, 90)

# Fewer, stronger edges: raise thresholds
coarse = cv2.Canny(blur, 100, 200)

# L2 gradient uses sqrt(Gx^2+Gy^2) internally for magnitude
edges_l2 = cv2.Canny(blur, 50, 150, apertureSize=3, L2gradient=True)

# Larger Sobel aperture inside Canny (5 or 7): slightly smoother gradients
edges_ap5 = cv2.Canny(blur, 50, 150, apertureSize=5)

Practical pipeline

For document or industrial images, thresholds are easier to set if intensities are normalized. One pattern: blur → optional CLAHE (see histogram chapter) → Canny. For color images, either convert to grayscale or run Canny per channel and combine with bitwise OR.

import cv2
import numpy as np

bgr = cv2.imread("scene.jpg")
gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)

med = np.median(gray)
t1 = int(max(0, 0.66 * med))
t2 = int(min(255, 1.33 * med))
auto_edges = cv2.Canny(gray, t1, t2)

Median-based heuristic for thresholds—starting point only; validate on your data.

Takeaways

  • Use float Sobel/Scharr, then visualize with convertScaleAbs or manual scaling.
  • Canny needs two thresholds and benefits from light pre-blur.
  • Tune thresholds per resolution and lighting; consider auto heuristics then refine.

Quick FAQ

Sobel gives thick “ridges” of high gradient; Canny thins them and links with hysteresis for cleaner contours. For many segmentation pre-steps, Canny is easier to use out of the box.

Too little blur → noise becomes edges. Too much → true edges weaken and disappear. Match blur σ to noise scale and image resolution.