Edge maps isolate rapid brightness changes so object boundaries can feed contour finding, shape analysis, or visual inspection. OpenCV implements the Canny detector as cv.Canny(), which produces thin white edges on a black background.
Noise can create short unwanted edges before the detector evaluates gradient strength. A small Gaussian blur reduces that noise, while the lower and upper Canny thresholds decide which weak gradients remain connected to strong edges.
The Python program reads one grayscale PNG and saves the edge map as another PNG, so it works without a desktop window. Thresholds of 80 and 160 are a useful starting pair for the sample scene; images with faint boundaries or heavy texture may need different values.
Related: How to read and write an image with OpenCV
Related: How to blur an image with OpenCV
Related: How to find contours with OpenCV
Images with visible brightness changes around objects or structures provide suitable boundaries for the edge map.
from pathlib import Path import cv2 as cv input_path = Path("input/scene.png") output_path = Path("output/edges.png") gray = cv.imread(str(input_path), cv.IMREAD_GRAYSCALE) if gray is None: raise SystemExit(f"could not read image: {input_path}")
smoothed = cv.GaussianBlur(gray, (5, 5), 0) edges = cv.Canny(smoothed, 80, 160)
The 5 x 5 Gaussian kernel reduces isolated noise before Canny evaluates gradients. The lower threshold keeps weak edges only when they connect to edges that reach the upper threshold.
output_path.parent.mkdir(parents=True, exist_ok=True) if not cv.imwrite(str(output_path), edges): raise SystemExit(f"could not write image: {output_path}") edge_pixels = cv.countNonZero(edges) edge_percent = edge_pixels / edges.size * 100 print(f"input: {input_path}") print(f"image size: {gray.shape[1]}x{gray.shape[0]}") print("thresholds: 80/160") print(f"edge pixels: {edge_pixels} ({edge_percent:.2f}%)") print(f"output: {output_path}")
from pathlib import Path import cv2 as cv input_path = Path("input/scene.png") output_path = Path("output/edges.png") gray = cv.imread(str(input_path), cv.IMREAD_GRAYSCALE) if gray is None: raise SystemExit(f"could not read image: {input_path}") smoothed = cv.GaussianBlur(gray, (5, 5), 0) edges = cv.Canny(smoothed, 80, 160) output_path.parent.mkdir(parents=True, exist_ok=True) if not cv.imwrite(str(output_path), edges): raise SystemExit(f"could not write image: {output_path}") edge_pixels = cv.countNonZero(edges) edge_percent = edge_pixels / edges.size * 100 print(f"input: {input_path}") print(f"image size: {gray.shape[1]}x{gray.shape[0]}") print("thresholds: 80/160") print(f"edge pixels: {edge_pixels} ({edge_percent:.2f}%)") print(f"output: {output_path}")
$ python3 detect_edges.py input: input/scene.png image size: 720x480 thresholds: 80/160 edge pixels: 3832 (1.11%) output: output/edges.png
$ python3 -c 'import cv2 as cv; image = cv.imread("output/edges.png", cv.IMREAD_GRAYSCALE); print(image.shape); print(image.min(), image.max()); print(cv.countNonZero(image))'
(480, 720)
0 255
3832
A missing image causes this command to fail, while a zero edge count shows that the selected thresholds retained no boundaries.