How to detect edges in an image with OpenCV

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.

Steps to detect image edges with OpenCV:

  1. Place the source image at input/scene.png.

    Images with visible brightness changes around objects or structures provide suitable boundaries for the edge map.

  2. Create detect_edges.py with the input contract and grayscale image check.
    detect_edges.py
    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}")
  3. Append the smoothing and Canny stages below the image-read check in detect_edges.py.
    detect_edges.py
    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.

  4. Append the PNG save and edge-count output below the cv.Canny() call in detect_edges.py.
    detect_edges.py
    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}")
  5. Compare detect_edges.py with the completed edge-detection program.
    detect_edges.py
    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}")
  6. Run the completed program to write the Canny edge map.
    $ python3 detect_edges.py
    input: input/scene.png
    image size: 720x480
    thresholds: 80/160
    edge pixels: 3832 (1.11%)
    output: output/edges.png

  7. Confirm that the saved edge map contains both background and detected-edge pixels.
    $ 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.