CV2 Python - image merging based on homography matrix - error in mergeing

29 Views Asked by At

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:

Merged images (better quality, but the upload size is limited)

Second created image: 3 merged image, the image is not showed under the black border (oficially transparent)

Thanks in advance!

0

There are 0 best solutions below