I am working on a project where I need to merge some (more than 1000) orthophotos. First of all I would like to know what my options are, because I think I haven't found all the solutions.
Secondly, I have a specific problem. My problem is that when I want to add a second image to the first one attached, the background (which is originally transparent) hides the third image. I think, my errors are in the placeing method.
My ultimate goal is to put this image on a map using QGIS, but it's not the best one by one, it would be better to merge the images into fewer pieces
This code generates images like the first:
import cv2
import numpy as np
# Load the images
img1 = cv2.imread('out1-163.png')
img2 = cv2.imread('out1-160.png')
# Load the images
image2 = img1
image1 = img2
# Define the size of the border
top, bottom, left, right = [100]*4 # Adjust these values as needed
# Convert image1 to 4-channel RGBA
image1_rgba = cv2.cvtColor(image1, cv2.COLOR_BGR2BGRA)
# Create a transparent border around image1
image1_bordered = image1_rgba
# Convert image2 to 4-channel RGBA
image2_rgba = cv2.cvtColor(image2, cv2.COLOR_BGR2BGRA)
# Create a transparent border around image2
image2_bordered = image2_rgba
# Convert images to grayscale
gray1 = cv2.cvtColor(image1_bordered, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2_bordered, cv2.COLOR_BGR2GRAY)
# Initialize the feature detector and extractor (e.g., SIFT)
sift = cv2.SIFT_create()
# Detect keypoints and compute descriptors for both images
keypoints1, descriptors1 = sift.detectAndCompute(gray1, None)
keypoints2, descriptors2 = sift.detectAndCompute(gray2, None)
# Initialize the feature matcher using brute-force matching
bf = cv2.BFMatcher()
# Match the descriptors using brute-force matching
matches = bf.match(descriptors1, descriptors2)
# Select the top N matches
num_matches = 50
matches = sorted(matches, key=lambda x: x.distance)[:num_matches]
# Extract matching keypoints
src_points = np.float32([keypoints1[match.queryIdx].pt for match in matches]).reshape(-1, 1, 2)
dst_points = np.float32([keypoints2[match.trainIdx].pt for match in matches]).reshape(-1, 1, 2)
# Estimate the homography matrix
homography, _ = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5.0)
# Calculate the size of the new image
corners = np.array([
[0, 0],
[0, image1_bordered.shape[0]],
[image1_bordered.shape[1], 0],
[image1_bordered.shape[1], image1_bordered.shape[0]]
], dtype=np.float32)
corners = cv2.perspectiveTransform(corners.reshape(-1, 1, 2), homography).reshape(-1, 2)
min_x, min_y = corners.min(axis=0).astype(int)
max_x, max_y = corners.max(axis=0).astype(int)
width = max(max_x, image2_bordered.shape[1]) - min_x
height = max(max_y, image2_bordered.shape[0]) - min_y
# Adjust the homography matrix to shift the image to the right place
homography[0, 2] -= min_x
homography[1, 2] -= min_y
# Warp image1 to image2 using the adjusted homography matrix
result = cv2.warpPerspective(image1_bordered, homography, (width, height))
# Overlay image2 on the warped image1
startY = max(-min_y, 0)
startX = max(-min_x, 0)
endY = startY + image2_bordered.shape[0]
endX = startX + image2_bordered.shape[1]
# Replace the corresponding pixels in the first image with the pixels from the second image
result[startY:endY, startX:endX] = image2_bordered[:endY-startY, :endX-startX]
# Save the result
cv2.imwrite('merged.png', result)
Here is the 3rd image mergeing code:
import cv2
import numpy as np
# Load the images
img1 = cv2.imread('merged.png', cv2.IMREAD_UNCHANGED)
img2 = cv2.imread('out1-164.png')
# Load the images
image2 = img1
image1 = img2
# Define the size of the border
top, bottom, left, right = [100]*4 # Adjust these values as needed
# Convert image1 to 4-channel RGBA
image1_rgba = cv2.cvtColor(image1, cv2.COLOR_BGR2BGRA)
# Create a transparent border around image1
image1_bordered = image1_rgba
# Convert image2 to 4-channel RGBA
image2_rgba = cv2.cvtColor(image2, cv2.COLOR_BGR2BGRA)
# Create a transparent border around image2
image2_bordered = image2_rgba
# Convert images to grayscale
gray1 = cv2.cvtColor(image1_bordered, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2_bordered, cv2.COLOR_BGR2GRAY)
# Initialize the feature detector and extractor (e.g., SIFT)
sift = cv2.SIFT_create()
# Detect keypoints and compute descriptors for both images
keypoints1, descriptors1 = sift.detectAndCompute(gray1, None)
keypoints2, descriptors2 = sift.detectAndCompute(gray2, None)
# Initialize the feature matcher using brute-force matching
bf = cv2.BFMatcher()
# Match the descriptors using brute-force matching
matches = bf.match(descriptors1, descriptors2)
# Select the top N matches
num_matches = 50
matches = sorted(matches, key=lambda x: x.distance)[:num_matches]
# Extract matching keypoints
src_points = np.float32([keypoints1[match.queryIdx].pt for match in matches]).reshape(-1, 1, 2)
dst_points = np.float32([keypoints2[match.trainIdx].pt for match in matches]).reshape(-1, 1, 2)
# Estimate the homography matrix
homography, _ = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5.0)
# Calculate the size of the new image
corners = np.array([
[0, 0],
[0, image1_bordered.shape[0]],
[image1_bordered.shape[1], 0],
[image1_bordered.shape[1], image1_bordered.shape[0]]
], dtype=np.float32)
corners = cv2.perspectiveTransform(corners.reshape(-1, 1, 2), homography).reshape(-1, 2)
min_x, min_y = corners.min(axis=0).astype(int)
max_x, max_y = corners.max(axis=0).astype(int)
width = max(max_x, image2_bordered.shape[1]) - min_x
height = max(max_y, image2_bordered.shape[0]) - min_y
# Adjust the homography matrix to shift the image to the right place
homography[0, 2] -= min_x
homography[1, 2] -= min_y
# Warp image1 to image2 using the adjusted homography matrix
result = cv2.warpPerspective(image1_bordered, homography, (width, height))
# Overlay image2 on the warped image1
startY = max(-min_y, 0)
startX = max(-min_x, 0)
endY = startY + image2_bordered.shape[0]
endX = startX + image2_bordered.shape[1]
# Ensure endY and endX do not exceed the size of the result image
endY = min(endY, result.shape[0])
endX = min(endX, result.shape[1])
# Replace the corresponding pixels in the first image with the pixels from the second image
result[startY:endY, startX:endX] = image2_bordered[:(endY-startY), :(endX-startX)]
# Create a mask of image2_bordered
mask = image2_bordered[..., 3] > 0
# Create a mask of non-transparent pixels in image2_bordered
mask = image2_bordered[..., 3] > 0
# Replace the non-transparent pixels in the first image with the pixels from the second image
for c in range(0, 3):
result[startY:endY, startX:endX, c] = np.where(mask[:(endY-startY), :(endX-startX)],
image2_bordered[:(endY-startY), :(endX-startX), c],
result[startY:endY, startX:endX, c])
# Save the result
cv2.imwrite('merged2_image.png', result)
My first created image:
Thanks in advance!

