Stitching Multiple Images to Create a Panorama using OpenCV and Python

Panorama

Creating a panorama, a wide-angle view of a scene by stitching multiple images together, has become increasingly popular in photography. With the advancements in computer vision, we can now automate this process using OpenCV, an open-source library for computer vision tasks, and Python. In this article, we will explore how to stitch multiple images together to create a beautiful panorama.

Getting Started

To begin, ensure that you have OpenCV installed on your Python environment. You can do this by running the following command:

pip install opencv-python

Next, we need to import the necessary packages:

import cv2
import numpy as np

Loading and Preprocessing the Images

First, we need to load the images that we want to stitch. In this example, let's consider we have three images: image1.jpg, image2.jpg, and image3.jpg.

image1 = cv2.imread('image1.jpg')
image2 = cv2.imread('image2.jpg')
image3 = cv2.imread('image3.jpg')

Since the images might have slight variations in lighting or exposure, it is recommended to perform some preprocessing steps, such as resizing and grayscale conversion, to enhance the results.

image1 = cv2.resize(image1, (0, 0), fx=0.5, fy=0.5)
image2 = cv2.resize(image2, (0, 0), fx=0.5, fy=0.5)
image3 = cv2.resize(image3, (0, 0), fx=0.5, fy=0.5)

gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
gray3 = cv2.cvtColor(image3, cv2.COLOR_BGR2GRAY)

Feature Detection and Description

To stitch the images together, we need to find corresponding features among them. We can use the SIFT (Scale-Invariant Feature Transform) algorithm provided by OpenCV to detect and describe these features.

sift = cv2.SIFT_create()

keypoints1, descriptors1 = sift.detectAndCompute(gray1, None)
keypoints2, descriptors2 = sift.detectAndCompute(gray2, None)
keypoints3, descriptors3 = sift.detectAndCompute(gray3, None)

Matching Features

The next step is to match the features among the images. We can use the FLANN (Fast Library for Approximate Nearest Neighbors) algorithm to efficiently find the matches.

flann = cv2.FlannBasedMatcher()

matches12 = flann.knnMatch(descriptors1, descriptors2, k=2)
matches23 = flann.knnMatch(descriptors2, descriptors3, k=2)

Filtering Matches

To ensure the accuracy of the stitching process, we need to filter the matches by applying different techniques such as RANSAC (Random Sample Consensus) or Lowe's ratio test. In this example, we will use the ratio test.

ratio_threshold = 0.7

good_matches12 = []
for m, n in matches12:
    if m.distance < ratio_threshold * n.distance:
        good_matches12.append(m)

good_matches23 = []
for m, n in matches23:
    if m.distance < ratio_threshold * n.distance:
        good_matches23.append(m)

Image Alignment and Stitching

Finally, we need to align the images based on the filtered matches and stitch them together to create the panorama. We can use the homography matrix provided by OpenCV to align the images.

MIN_MATCH_COUNT = 10

if len(good_matches12) > MIN_MATCH_COUNT and len(good_matches23) > MIN_MATCH_COUNT:
    src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches12]).reshape(-1, 1, 2)
    dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches12]).reshape(-1, 1, 2)
    M12, mask12 = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

    src_pts = np.float32([keypoints2[m.queryIdx].pt for m in good_matches23]).reshape(-1, 1, 2)
    dst_pts = np.float32([keypoints3[m.trainIdx].pt for m in good_matches23]).reshape(-1, 1, 2)
    M23, mask23 = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

    # Warp images
    height, width = image1.shape[:2]
    warped_image2 = cv2.warpPerspective(image2, M12, (width, height))

    height, width = image2.shape[:2]
    warped_image3 = cv2.warpPerspective(image3, M23, (width, height))

    # Combine images
    panorama = np.zeros((height, width, 3), dtype=np.uint8)
    panorama[:image1.shape[0], :image1.shape[1]] = image1
    panorama[:image2.shape[0], :image2.shape[1]] = warped_image2
    panorama[:image3.shape[0], :image3.shape[1]] = warped_image3

Displaying the Panorama

Finally, we can display the generated panorama using OpenCV.

cv2.imshow('Panorama', panorama)
cv2.waitKey(0)
cv2.destroyAllWindows()

Conclusion

Stitching multiple images together to create a panorama has become easier than ever with the help of OpenCV and Python. By utilizing feature detection, matching, filtering, and image alignment techniques, we can seamlessly combine images to provide a wide-angle view. So, grab your camera, capture a series of images, and let OpenCV do the magic of stitching them into a breathtaking panorama!

Happy stitching!


noob to master © copyleft