How to stitch images with OpenCV

Panorama stitching needs input images that share enough recognizable scene detail for feature matching. OpenCV provides a high-level stitcher that can align overlapping photos and blend them into one wider image, which helps when a Python script needs to check whether a capture set can become a panorama.

The Python script uses cv2.Stitcher_create() in PANORAMA mode, which OpenCV uses for camera photos under perspective transformation. The script reads image paths from the command line, runs stitch(), stops on a nonzero status code, and writes the panorama only after OpenCV reports OK.

Input quality controls the outcome. Use photos with visible overlap, textured details, similar exposure, and limited moving subjects; status values such as ERR_NEED_MORE_IMGS or ERR_HOMOGRAPHY_EST_FAIL mean the image set needs more overlap, more features, or fewer mismatched frames.

Steps to stitch images with OpenCV:

  1. Place at least two overlapping source images in the sample directory.

    The run command uses samples/stitch-left.png and samples/stitch-right.png. Replace those paths with the ordered image list for the real panorama.

  2. Create the stitching script.
    stitch_images.py
    #!/usr/bin/env python3
    import argparse
    from pathlib import Path
     
    import cv2
     
     
    STATUS_NAMES = {
        0: "OK",
        1: "ERR_NEED_MORE_IMGS",
        2: "ERR_HOMOGRAPHY_EST_FAIL",
        3: "ERR_CAMERA_PARAMS_ADJUST_FAIL",
    }
     
     
    def read_image(path):
        image = cv2.imread(str(path))
        if image is None:
            raise SystemExit(f"cannot_read: {path}")
        return image
     
     
    def main():
        parser = argparse.ArgumentParser()
        parser.add_argument("images", nargs="+", type=Path)
        parser.add_argument("--output", default=Path("output/panorama.png"), type=Path)
        args = parser.parse_args()
     
        if len(args.images) < 2:
            raise SystemExit("need_at_least_two_images")
     
        images = [read_image(path) for path in args.images]
     
        stitcher = cv2.Stitcher_create(cv2.Stitcher_PANORAMA)
        status, panorama = stitcher.stitch(images)
     
        print(f"input_images: {len(images)}")
        print(f"stitcher_status: {status} ({STATUS_NAMES.get(status, 'UNKNOWN')})")
     
        if status != cv2.Stitcher_OK:
            raise SystemExit("stitch_failed")
     
        args.output.parent.mkdir(parents=True, exist_ok=True)
        if not cv2.imwrite(str(args.output), panorama):
            raise SystemExit(f"cannot_write: {args.output}")
     
        height, width = panorama.shape[:2]
        print(f"saved_panorama: {args.output}")
        print(f"output_shape: {width}x{height}")
     
     
    if __name__ == "__main__":
        main()
  3. Run the stitcher against the ordered image list.
    $ python3 stitch_images.py --output output/panorama.png samples/stitch-left.png samples/stitch-right.png
    input_images: 2
    stitcher_status: 0 (OK)
    saved_panorama: output/panorama.png
    output_shape: 998x420

    stitcher_status: 0 (OK) confirms that OpenCV created the panorama. A nonzero status exits before writing the output image.

  4. Open the saved panorama and check the overlap area.

    The image should show one blended canvas instead of two separate frames. If the overlap is warped, cropped incorrectly, or missing, retry with images that share more textured scene detail.