Spaces:
Runtime error
Runtime error
Commit ·
6cfbe50
1
Parent(s): 155378b
Create Image_stitching.py
Browse files- Image_stitching.py +94 -0
Image_stitching.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
import matplotlib.pyplot as plt
|
| 4 |
+
import pdb
|
| 5 |
+
|
| 6 |
+
def rotate_to_horizontal(image):
|
| 7 |
+
# Rotate the image to horizontal (90 degrees counterclockwise)
|
| 8 |
+
rotated_image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
| 9 |
+
return rotated_image
|
| 10 |
+
|
| 11 |
+
def rotate_to_vertical(image):
|
| 12 |
+
# Rotate the image back to vertical (90 degrees clockwise)
|
| 13 |
+
rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
|
| 14 |
+
return rotated_image
|
| 15 |
+
|
| 16 |
+
def plot_images(images):
|
| 17 |
+
num_images = len(images)
|
| 18 |
+
|
| 19 |
+
for i, image in enumerate(images):
|
| 20 |
+
plt.figure(figsize=(6, 6))
|
| 21 |
+
plt.imshow(image)
|
| 22 |
+
plt.title(f"Image {i+1}/{num_images}")
|
| 23 |
+
plt.axis('off')
|
| 24 |
+
plt.show()
|
| 25 |
+
|
| 26 |
+
def image_stitching(image_paths):
|
| 27 |
+
images = [cv2.imread(path) for path in image_paths]
|
| 28 |
+
|
| 29 |
+
images = [image.astype(np.uint8) for image in images]
|
| 30 |
+
images = [rotate_to_horizontal(image) for image in images]
|
| 31 |
+
images = list(reversed(images))
|
| 32 |
+
if len(images) == 15:
|
| 33 |
+
images = images[2:12]
|
| 34 |
+
#plot_images(images)
|
| 35 |
+
|
| 36 |
+
# Create a list to store the stitched images
|
| 37 |
+
stitched_images = []
|
| 38 |
+
|
| 39 |
+
# Accumulated homography matrix for stitching
|
| 40 |
+
accumulated_homography = np.eye(3)
|
| 41 |
+
|
| 42 |
+
# Iterate through pairs of adjacent images and stitch them together
|
| 43 |
+
for i in range(len(images) - 1):
|
| 44 |
+
# Perform keypoint and feature descriptor extraction
|
| 45 |
+
orb = cv2.ORB_create()
|
| 46 |
+
keypoints_and_descriptors = [orb.detectAndCompute(image, None) for image in [images[i], images[i + 1]]]
|
| 47 |
+
|
| 48 |
+
# Match the keypoints using Brute-Force Matcher
|
| 49 |
+
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
|
| 50 |
+
matches = bf.match(keypoints_and_descriptors[0][1], keypoints_and_descriptors[1][1])
|
| 51 |
+
|
| 52 |
+
# Filter the matches to remove outliers using RANSAC
|
| 53 |
+
src_pts = np.float32([keypoints_and_descriptors[0][0][m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
|
| 54 |
+
dst_pts = np.float32([keypoints_and_descriptors[1][0][m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
|
| 55 |
+
|
| 56 |
+
M, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
|
| 57 |
+
|
| 58 |
+
# Accumulate the homography matrices
|
| 59 |
+
accumulated_homography = np.dot(M, accumulated_homography)
|
| 60 |
+
|
| 61 |
+
# Warp perspective and stitch the images
|
| 62 |
+
stitched_image = cv2.warpPerspective(images[i + 1], accumulated_homography,
|
| 63 |
+
(images[i].shape[1] + images[i + 1].shape[1], images[i].shape[0]))
|
| 64 |
+
stitched_image[0:images[i].shape[0], 0:images[i].shape[1]] = images[i]
|
| 65 |
+
|
| 66 |
+
# Remove the empty pixels and retain maximum image information
|
| 67 |
+
# stitched_image = remove_empty_pixels(stitched_image)
|
| 68 |
+
|
| 69 |
+
stitched_images.append(stitched_image)
|
| 70 |
+
|
| 71 |
+
# Combine all stitched images into a final panorama
|
| 72 |
+
final_panorama = stitched_images[0]
|
| 73 |
+
for i in range(1, len(stitched_images)):
|
| 74 |
+
final_panorama = cv2.warpPerspective(stitched_images[i], np.eye(3),
|
| 75 |
+
(final_panorama.shape[1] + stitched_images[i].shape[1], final_panorama.shape[0]))
|
| 76 |
+
final_panorama[0:stitched_images[i].shape[0], 0:stitched_images[i].shape[1]] = stitched_images[i]
|
| 77 |
+
|
| 78 |
+
gray_images = [cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) for image in images[:2]]
|
| 79 |
+
|
| 80 |
+
# Draw the keypoints on the images
|
| 81 |
+
keypoints_drawn = [cv2.drawKeypoints(gray_image, kp[0], None, color=(0, 255, 0),
|
| 82 |
+
flags=cv2.DrawMatchesFlags_DRAW_RICH_KEYPOINTS) for gray_image, kp in
|
| 83 |
+
zip(gray_images, keypoints_and_descriptors[:2])]
|
| 84 |
+
|
| 85 |
+
# Draw the matches on the images
|
| 86 |
+
matches_drawn = cv2.drawMatches(images[0], keypoints_and_descriptors[0][0], images[1],
|
| 87 |
+
keypoints_and_descriptors[1][0], matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
|
| 88 |
+
|
| 89 |
+
# Crop the final image to 512x512 centered around the middle
|
| 90 |
+
cropped_final_panorama = final_panorama[:512, :512]
|
| 91 |
+
rotated_final_panorama = rotate_to_vertical(cropped_final_panorama)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
return rotated_final_panorama
|