Python Panorama Stitching Using OpenCV and SIFT

This article explains how to create a panoramic image by detecting SIFT keypoints, matching them with KNN, estimating a homography using RANSAC, and warping and blending two overlapping photos with OpenCV in Python.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Python Panorama Stitching Using OpenCV and SIFT

Panorama stitching combines two overlapping images into a single wide‑view picture by leveraging computer‑vision techniques such as SIFT keypoint detection, local invariant feature extraction, KNN matching, RANSAC‑based homography estimation, and perspective warping.

Specific steps :

Detect SIFT keypoints and extract descriptors from the left and right images.

Use knnMatch to find the two best matches for each descriptor and filter matches with a ratio test.

Compute the homography matrix H from the filtered point pairs and warp the right image to the left image’s coordinate system.

Overlay the left image onto the warped right image to obtain the final panorama.

The complete Python script that implements these steps is shown below. All code lines are kept unchanged and wrapped in a single

block.</p>
<code>import cv2 as cv  # import OpenCV package
import numpy as np  # import NumPy for matrix operations

# Detect SIFT keypoints
def sift_keypoints_detect(image):
    # Convert to grayscale
    gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    # Instantiate SIFT detector
    sift = cv.xfeatures2d.SIFT_create()
    # Detect keypoints and compute descriptors
    keypoints, features = sift.detectAndCompute(image, None)
    # Draw keypoints on the image
    keypoints_image = cv.drawKeypoints(gray_image, keypoints, None, flags=cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
    return keypoints_image, keypoints, features

# Match features using KNN and ratio test
def get_feature_point_ensemble(features_right, features_left):
    bf = cv.BFMatcher()
    matches = bf.knnMatch(features_right, features_left, k=2)
    matches = sorted(matches, key=lambda x: x[0].distance / x[1].distance)
    good = []
    for m, n in matches:
        ratio = 0.6
        if m.distance < ratio * n.distance:
            good.append(m)
    return good

# Panorama stitching function
def Panorama_stitching(image_right, image_left):
    _, _, features_right = sift_keypoints_detect(image_right)
    _, _, features_left = sift_keypoints_detect(image_left)
    goodMatch = get_feature_point_ensemble(features_right, features_left)
    if len(goodMatch) > 4:
        ptsR = np.float32([keypoints_right[m.queryIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
        ptsL = np.float32([keypoints_left[m.trainIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
        ransacReprojThreshold = 4
        Homography, status = cv.findHomography(ptsR, ptsL, cv.RANSAC, ransacReprojThreshold)
        Panorama = cv.warpPerspective(image_right, Homography, (image_right.shape[1] + image_left.shape[1], image_right.shape[0]))
        Panorama[0:image_left.shape[0], 0:image_left.shape[1]] = image_left
        return Panorama

if __name__ == '__main__':
    image_left = cv.imread('./Left.jpg')
    image_right = cv.imread('./Right.jpg')
    # Resize to make dimensions consistent
    image_right = cv.resize(image_right, None, fx=0.4, fy=0.24)
    image_left = cv.resize(image_left, (image_right.shape[1], image_right.shape[0]))
    # Detect and display keypoints
    kp_img_right, kp_right, feat_right = sift_keypoints_detect(image_right)
    kp_img_left, kp_left, feat_left = sift_keypoints_detect(image_left)
    cv.imshow('Left Image Keypoints', np.hstack((image_left, kp_img_left)))
    cv.waitKey(0); cv.destroyAllWindows()
    cv.imshow('Right Image Keypoints', np.hstack((image_right, kp_img_right)))
    cv.waitKey(0); cv.destroyAllWindows()
    # Match and draw matches
    goodMatch = get_feature_point_ensemble(feat_right, feat_left)
    all_goodmatch_image = cv.drawMatches(image_right, kp_right, image_left, kp_left, goodMatch, None, None, None, None, flags=2)
    cv.imshow('All SIFT Matches', all_goodmatch_image)
    cv.waitKey(0); cv.destroyAllWindows()
    # Stitch and save panorama
    Panorama = Panorama_stitching(image_right, image_left)
    cv.namedWindow('Panorama', cv.WINDOW_AUTOSIZE)
    cv.imshow('Panorama', Panorama)
    cv.imwrite('./panorama.jpg', Panorama)
    cv.waitKey(0); cv.destroyAllWindows()

Running the script displays the detected keypoints for each input image, visualizes the matched feature pairs, shows the warped right image, and finally presents the stitched panorama, which may contain black borders on the right side when the overlap is large.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Computer VisionPythonSIFTpanorama stitching
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.