Python, OpenCV -- 对多张图像进行对齐和叠加

6
我的项目是将航空照片对齐,制作一张镶嵌地图。我的计划是从两张照片开始,将第二张与第一张对齐,并创建一个“初始镶嵌图”来组合这两个对齐的图像。完成后,我会将第三张照片与初始镶嵌图对齐,然后将第四张照片与结果对齐,以此类推,逐步构建地图。
我有两种方法来实现这一点,但更精确的方法需要使用calcOpticalFlowPyrLK(),只能用于两张图片的阶段,因为两张输入图片必须具有相同的大小。因此,我尝试了一种新的解决方案,但它不够准确,每一步引入的误差都会堆积,最终产生无意义的结果。
我的问题有两个方面,如果您知道其中一个答案,您可以只回答一个,除非您想回答两个。首先,是否有办法使用类似于calcOpticalFlowPyrLK()的东西,但是可以处理不同大小的两张图片(包括任何潜在的解决方法)?其次,是否有办法修改检测器/描述符解决方案,使其更加准确?
以下是仅适用于两张图片的精确版本:
# load images
base = cv2.imread("images/1.jpg")
curr = cv2.imread("images/2.jpg")

# convert to grayscale
base_gray = cv2.cvtColor(base, cv2.COLOR_BGR2GRAY)

# find the coordinates of good features to track  in base
base_features = cv2.goodFeaturesToTrack(base_gray, 3000, .01, 10)

# find corresponding features in current photo
curr_features = np.array([])
curr_features, pyr_stati, _ = cv2.calcOpticalFlowPyrLK(base, curr, base_features, curr_features, flags=1)

# only add features for which a match was found to the pruned arrays
base_features_pruned = []
curr_features_pruned = []
for index, status in enumerate(pyr_stati):
    if status == 1:
        base_features_pruned.append(base_features[index])
        curr_features_pruned.append(curr_features[index])

# convert lists to numpy arrays so they can be passed to opencv function
bf_final = np.asarray(base_features_pruned)
cf_final = np.asarray(curr_features_pruned)

# find perspective transformation using the arrays of corresponding points
transformation, hom_stati = cv2.findHomography(cf_final, bf_final, method=cv2.RANSAC, ransacReprojThreshold=1)

# transform the images and overlay them to see if they align properly
# not what I do in the actual program, just for use in the example code
# so that you can see how they align, if you decide to run it
height, width = curr.shape[:2]
mod_photo = cv2.warpPerspective(curr, transformation, (width, height))
new_image = cv2.addWeighted(mod_photo, .5, base, .5, 1)

以下是适用于多张图片但存在误差的方法:

# load images
base = cv2.imread("images/1.jpg")
curr = cv2.imread("images/2.jpg")


# convert to grayscale
base_gray = cv2.cvtColor(self.base, cv2.COLOR_BGR2GRAY)

# DIFFERENCES START
curr_gray = cv2.cvtColor(self.curr_photo, cv2.COLOR_BGR2GRAY)

# create detector, get keypoints and descriptors
detector = cv2.ORB_create()
base_keys, base_desc = detector.detectAndCompute(base_gray, None)
curr_keys, curr_desc = detector.detectAndCompute(curr_gray, None)

matcher = cv2.DescriptorMatcher_create("BruteForce-Hamming")

max_dist = 0.0
min_dist = 100.0

for match in matches:
     dist = match.distance
     min_dist = dist if dist < min_dist else min_dist
     max_dist = dist if dist > max_dist else max_dist

good_matches = [match for match in matches if match.distance <= 3 * min_dist ]

base_matches = []
curr_matches = []
for match in good_matches:
    base_matches.append(base_keys[match.queryIdx].pt)
    curr_matches.append(curr_keys[match.trainIdx].pt)

bf_final = np.asarray(base_matches)
cf_final = np.asarray(curr_matches)

# SAME AS BEFORE

# find perspective transformation using the arrays of corresponding points
transformation, hom_stati = cv2.findHomography(cf_final, bf_final, method=cv2.RANSAC, ransacReprojThreshold=1)

# transform the images and overlay them to see if they align properly
# not what I do in the actual program, just for use in the example code
# so that you can see how they align, if you decide to run it
height, width = curr.shape[:2]
mod_photo = cv2.warpPerspective(curr, transformation, (width, height))
new_image = cv2.addWeighted(mod_photo, .5, base, .5, 1)

最后,这里是我正在使用的一些图片: 输入图像描述 输入图像描述 输入图像描述 输入图像描述

Homographies可以组合。因此,如果您有从img1到img2的单应性“h12”和从img2到img3的单应性“h23”,则“h12.dot(h23)”是从img1到img3的单应性。 - alkasm
1个回答

9

同态学组合,因此如果您有img1img2之间以及img2img3之间的同态学,则这两个同态学的组合会给出img1img3之间的同态学。

当然,您的尺寸有误,因为您正在尝试将img3与包含img1img2的拼接图像进行匹配。但是您不需要这样做。在获得每对连续图像之间的所有同态学之后再进行拼接。然后您可以按照以下两种方式之一进行操作;从后往前或从前往后工作。例如我将使用h31来引用将img3扭曲成为img1坐标的同态学。

从前面(伪代码):

warp img2 into coordinates of img1 with h21
warp img3 into coordinates of img1 with h31 = h32 @ h21
warp img4 into coordinates of img1 with h41 = h43 @ h31
...
stitch/blend images together

这里的@是矩阵乘法运算符,它将实现我们的单应性合成(注意,最安全的方法是通过单应性的最后一个元素进行除法运算,以确保它们都被相同地缩放)。
从后面来看(伪代码):
...
warp prev stitched img into coordinates of img3 with h43
stitch warped stitched img with img3
warp prev stitched img into coordinates of img2 with h32
stitch warped stitched img with img2
warp prev stitched img into coordinates of img1 with h21
stitch warped stitched img with img1

这个想法要么是从前面开始,将所有内容扭曲成第一张图像的坐标框架中,要么是从后面开始,将其变形为前一张图像并拼接,然后再将拼接的图像变形为前一张图像,并重复此步骤。我认为第一种方法可能更容易。在任何情况下,您都必须担心误差在多个组合的单应性估计中的传播,因为它们会逐渐累积。

这是使用单应性简单融合多个图像的方法。更复杂的方法是使用捆绑调整,它考虑了所有图像中的特征点。然后进行增益补偿以消除相机增益调整和虚光效应,然后进行多频带混合以防止模糊。请参见Brown和Lowe的开创性论文这里,以及一个精彩的示例和免费演示软件这里


如何在C++中进行单应矩阵的点积运算?根据SDK,函数Mat.dot(Mat2)返回一个double类型的值。 - Manmohan Bishnoi
1
@ManmohanBishnoi 在OpenCV中,*运算符被重载用于矩阵乘法,因此您可以简单地执行mat1 * mat2。OpenCV中的.dot()方法纯粹用于两个向量的内积,产生一个标量值。我在这里使用的.dot()方法来自Numpy,当输入不是1-D数组时,它执行矩阵乘法。它的命名不好,我会相应地编辑我的代码! - alkasm
你的通过单应矩阵乘法拼接的方法,适用于增量全景图吗? - Manmohan Bishnoi
1
@ManmohanBishnoi 是的,这个答案特别提到了这一点。然而,整束调整是一种更好的方法。 - alkasm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接