Edges mark sharp intensity changes that often become the first boundary signal in a vision pipeline. In OpenCV, Canny edge detection turns a photo or generated scene into a binary edge map before contour finding, shape measurement, or visual inspection.
Canny works on a single-channel image, so the script loads the source as grayscale, applies a small Gaussian blur to reduce isolated noise, and passes two hysteresis thresholds to cv.Canny(). Pixels connected to strong gradients remain white in the output, while background and weak unconnected texture stay black.
The sample path uses input/scene.png and writes output/edges.png. The edge-pixel percentage gives a quick sanity check because a near-zero value usually means the thresholds are too high, while a crowded edge map usually means the thresholds are too low or the image needs more blur.
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
Steps to detect image edges with OpenCV:
- Place the source image at input/scene.png.
Use an image with visible object boundaries or scene structure. Canny works on grayscale data, so color is not required unless later project code needs it.
- Create detect_edges.py.
- detect_edges.py
#!/usr/bin/env python3 import argparse from pathlib import Path import cv2 as cv import numpy as np def odd_kernel(value: str) -> int: kernel = int(value) if kernel < 1 or kernel % 2 == 0: raise argparse.ArgumentTypeError("blur kernel must be a positive odd integer") return kernel parser = argparse.ArgumentParser(description="Detect image edges with OpenCV Canny.") parser.add_argument("input_image", type=Path) parser.add_argument("output_image", type=Path) parser.add_argument("--low", type=float, default=80.0) parser.add_argument("--high", type=float, default=160.0) parser.add_argument("--blur", type=odd_kernel, default=5) parser.add_argument("--aperture", type=odd_kernel, default=3) parser.add_argument("--l2-gradient", action="store_true") args = parser.parse_args() if args.aperture not in {3, 5, 7}: raise SystemExit("aperture must be 3, 5, or 7") if args.low >= args.high: raise SystemExit("--low must be lower than --high") gray = cv.imread(str(args.input_image), cv.IMREAD_GRAYSCALE) if gray is None: raise SystemExit(f"could not read image: {args.input_image}") source = cv.GaussianBlur(gray, (args.blur, args.blur), 0) edges = cv.Canny( source, args.low, args.high, apertureSize=args.aperture, L2gradient=args.l2_gradient, ) args.output_image.parent.mkdir(parents=True, exist_ok=True) if not cv.imwrite(str(args.output_image), edges): raise SystemExit(f"could not write image: {args.output_image}") edge_pixels = int(np.count_nonzero(edges)) total_pixels = edges.size edge_percent = edge_pixels / total_pixels * 100 print(f"input: {args.input_image}") print(f"image size: {gray.shape[1]}x{gray.shape[0]}") print(f"thresholds: {args.low:.0f}/{args.high:.0f}") print(f"blur kernel: {args.blur}x{args.blur}") print(f"aperture: {args.aperture}") print(f"edge pixels: {edge_pixels} ({edge_percent:.2f}%)") print(f"output: {args.output_image}")
- Run Canny edge detection and write the edge map.
$ python3 detect_edges.py input/scene.png output/edges.png input: input/scene.png image size: 720x480 thresholds: 80/160 blur kernel: 5x5 aperture: 3 edge pixels: 4968 (1.44%) output: output/edges.png
--low and --high set the hysteresis thresholds. Raise both values when texture becomes edges, or lower them when object boundaries disappear.
- Verify the saved edge map can be reopened.
$ python3 -c 'import cv2 as cv, numpy as np; edges = cv.imread("output/edges.png", cv.IMREAD_GRAYSCALE); print(edges.shape); print(np.unique(edges))' (480, 720) [ 0 255]The shape confirms the saved image height and width. The two pixel values show a binary edge map where white pixels are retained edges.
- Remove detect_edges.py after moving the edge detector into project code.
$ rm detect_edges.py
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.