Unit 04.00: Edges are gradients, not outlines
An edge detector finds where intensity changes. Whether that is an object boundary is your interpretation, not its output.
Gradients, in x and in y
Sobel measures directional gradient; Canny thresholds gradient magnitude into a binary edge map.
The code measures both on an image with one hard edge and one soft band.
import cv2
import numpy as np
image = np.zeros((32, 32), dtype="uint8")
image[:, 16:] = 200 # one hard edge
image[4:8, :] = 100 # a soft band
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
print(f"vertical edge, gradient in x: {abs(sobel_x[16, 16]):.0f}")
print(f"vertical edge, gradient in y: {abs(sobel_y[16, 16]):.0f}")
print(f"horizontal band, gradient in y: {abs(sobel_y[4, 8]):.0f}")
canny = cv2.Canny(image, 50, 150)
print(f"\nCanny marks {int((canny > 0).sum())} pixels as edges")
print("edges are where intensity CHANGES, not where an object is")
# An edge detector responds to a shadow, a reflection and a printed line
# exactly as it responds to an object boundary. It measures gradient; the
# interpretation is yours.
The vertical edge produces a large x-gradient and essentially no y-gradient. That directionality is what makes gradient operators composable into corner and orientation detectors.
What the detector cannot do is distinguish an object boundary from a shadow, a reflection, or a printed line. All four are intensity changes and all four produce edges.
The mistake this prevents
The mistake is treating an edge map as a segmentation. Edges are open curves that break wherever contrast drops, so an object boundary in an edge map is usually several disconnected fragments.
Takeaway
Edges are gradients. Shadows and printed lines produce them exactly as object boundaries do, and an edge map is not a closed region.
