2

在将 24 个拼接图像的结果拼接到下一个第 25 个图像后,我得到了如下所示的输出。在那之前缝合很好。

在此处输入图像描述

有谁知道为什么/什么时候拼接的输出是这样的?这样输出的可能性是什么?这可能是什么原因?

拼接代码遵循标准拼接步骤,例如查找关键点、描述符然后匹配点、计算单应性然后对图像进行变形。但我不明白为什么会出现这种输出。

拼接的核心部分如下:

detector = cv2.SIFT_create(400)
# find the keypoints and descriptors with SIFT
gray1 = cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY)
ret1, mask1 = cv2.threshold(gray1,1,255,cv2.THRESH_BINARY)
kp1, descriptors1 = detector.detectAndCompute(gray1,mask1)

gray2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY)
ret2, mask2 = cv2.threshold(gray2,1,255,cv2.THRESH_BINARY)
kp2, descriptors2 = detector.detectAndCompute(gray2,mask2)

keypoints1Im = cv2.drawKeypoints(image1, kp1, outImage = cv2.DRAW_MATCHES_FLAGS_DEFAULT, color=(0,0,255))
keypoints2Im = cv2.drawKeypoints(image2, kp2, outImage = cv2.DRAW_MATCHES_FLAGS_DEFAULT, color=(0,0,255))

# BFMatcher with default params
matcher = cv2.BFMatcher()
matches = matcher.knnMatch(descriptors2,descriptors1, k=2)

# Apply ratio test
good = []
for m, n in matches:
    if m.distance < 0.75 * n.distance:
        good.append(m)

print (str(len(good)) + " Matches were Found")

if len(good) <= 10:
    return image1

matches = copy.copy(good)

matchDrawing = util.drawMatches(gray2,kp2,gray1,kp1,matches)

#Aligning the images
src_pts = np.float32([ kp2[m.queryIdx].pt for m in matches ]).reshape(-1,1,2)
dst_pts = np.float32([ kp1[m.trainIdx].pt for m in matches ]).reshape(-1,1,2)


H = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)[0]

h1,w1 = image1.shape[:2]
h2,w2 = image2.shape[:2]
pts1 = np.float32([[0,0],[0,h1],[w1,h1],[w1,0]]).reshape(-1,1,2)
pts2 = np.float32([[0,0],[0,h2],[w2,h2],[w2,0]]).reshape(-1,1,2)
pts2_ = cv2.perspectiveTransform(pts2, H)
pts = np.concatenate((pts1, pts2_), axis=0)
# print("pts:", pts)
[xmin, ymin] = np.int32(pts.min(axis=0).ravel() - 0.5)
[xmax, ymax] = np.int32(pts.max(axis=0).ravel() + 0.5)
t = [-xmin,-ymin]
Ht = np.array([[1,0,t[0]],[0,1,t[1]],[0,0,1]]) # translate

result = cv2.warpPerspective(image2, Ht.dot(H), (xmax-xmin, ymax-ymin))

resizedB = np.zeros((result.shape[0], result.shape[1], 3), np.uint8)

resizedB[t[1]:t[1]+h1,t[0]:w1+t[0]] = image1
# Now create a mask of logo and create its inverse mask also
img2gray = cv2.cvtColor(result,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 0, 255, cv2.THRESH_BINARY)

kernel = np.ones((5,5),np.uint8)
k1 = (kernel == 1).astype('uint8')
mask = cv2.erode(mask, k1, borderType=cv2.BORDER_CONSTANT)

mask_inv = cv2.bitwise_not(mask)

difference = cv2.bitwise_or(resizedB, resizedB, mask=mask_inv)

result2 = cv2.bitwise_and(result, result, mask=mask)

result = cv2.add(result2, difference)

编辑:

此图像显示匹配绘图,同时缝合 25 以生成 24 个图像:
在此处输入图像描述

在那场比赛之前:
在此处输入图像描述

我总共有 97 张图像要缝合。如果我分别缝合 24 和 25 图像,它们会正确缝合。如果我从第 23 张图像开始缝合,那么缝合也很好,但是当我从第 1 张图像开始缝合时它会给我带来问题。我无法理解这个问题。

拼接第 23 张图像后的结果:
在此处输入图像描述

拼接第 24 张图像后的结果:
在此处输入图像描述

拼接第 25 张图像后的结果如上所示,其中出错了。

奇怪的观察:如果我用相同的代码分别缝合 23、24、25 个图像,它会被缝合。如果我在 23 到 97 之后缝合图像,它会被缝合。但不知何故,如果我从第 1 个图像缝合图像,它会在缝合第 25 个图像时中断。我不明白为什么会这样。

我尝试了不同的组合,例如不同的关键点检测、提取方法、匹配方法、不同的单应性计算、不同的扭曲代码,但这些组合不起作用。步骤组合代码中缺少某些内容或错误。我无法弄清楚。

对不起这个长长的问题。由于我对此完全陌生,因此我无法正确解释和理解这些内容。感谢您的帮助和指导。

使用相同代码分别拼接 23、24、25 幅图像的结果:
在此处输入图像描述

使用不同的代码(在缝合之间给出黑线),如果我缝合了 97 个图像,那么第 25 个在缝合和缝合中上升,如下所示(右角点):
在此处输入图像描述

4

1 回答 1

5

首先,我无法重现您的问题并解决它,因为图像太大,我的系统无法处理。但是,我在全景拼接项目中遇到了同样的问题,所以我分享它背后的原因以及我解决问题的方法。希望这对你也有帮助。

当我像您一样将 4 张图像拼接在一起时,这就是我的问题。

我的问题

正如你所看到的,第四张图像被扭曲了很多,这是绝对不能发生的。同样的事情也发生在你身上,但在更大的层面上。

现在,这是我在经过一些图像预处理后拼接 8 张图像时的输出。

图像预处理后的输出

在对输入图像进行一些预处理后,我能够将 8 张图像完美地拼接在一起,没有任何失真。

要了解这种失真背后的确切原因,请在 50:26 - 1:07:23 之间观看Joseph Redmon 的这段视频

正如视频中所建议的,我们首先必须将图像投影到圆柱体上,然后展开它们,然后将这些展开的图像缝合在一起。

下面是初始输入图像(左)和投影并展开到圆柱体上后的图像(右)。

预处理前后的图像

对于您的问题,当您使用卫星图像时,我想投影到球体上会比圆柱体更好,但是您必须尝试一下。

在下面分享我的代码,用于将图像投影到圆柱体上并展开以供参考。它背后使用的数学与视频中给出的相同。


def Convert_xy(x, y):
    global center, f

    xt = ( f * np.tan( (x - center[0]) / f ) ) + center[0]
    yt = ( (y - center[1]) / np.cos( (x - center[0]) / f ) ) + center[1]
    
    return xt, yt


def ProjectOntoCylinder(InitialImage):
    global w, h, center, f
    h, w = InitialImage.shape[:2]
    center = [w // 2, h // 2]
    f = 1100       # 1100 field; 1000 Sun; 1500 Rainier; 1050 Helens
    
    # Creating a blank transformed image
    TransformedImage = np.zeros(InitialImage.shape, dtype=np.uint8)
    
    # Storing all coordinates of the transformed image in 2 arrays (x and y coordinates)
    AllCoordinates_of_ti =  np.array([np.array([i, j]) for i in range(w) for j in range(h)])
    ti_x = AllCoordinates_of_ti[:, 0]
    ti_y = AllCoordinates_of_ti[:, 1]
    
    # Finding corresponding coordinates of the transformed image in the initial image
    ii_x, ii_y = Convert_xy(ti_x, ti_y)

    # Rounding off the coordinate values to get exact pixel values (top-left corner)
    ii_tl_x = ii_x.astype(int)
    ii_tl_y = ii_y.astype(int)

    # Finding transformed image points whose corresponding 
    # initial image points lies inside the initial image
    GoodIndices = (ii_tl_x >= 0) * (ii_tl_x <= (w-2)) * \
                  (ii_tl_y >= 0) * (ii_tl_y <= (h-2))

    # Removing all the outside points from everywhere
    ti_x = ti_x[GoodIndices]
    ti_y = ti_y[GoodIndices]
    
    ii_x = ii_x[GoodIndices]
    ii_y = ii_y[GoodIndices]

    ii_tl_x = ii_tl_x[GoodIndices]
    ii_tl_y = ii_tl_y[GoodIndices]

    # Bilinear interpolation
    dx = ii_x - ii_tl_x
    dy = ii_y - ii_tl_y

    weight_tl = (1.0 - dx) * (1.0 - dy)
    weight_tr = (dx)       * (1.0 - dy)
    weight_bl = (1.0 - dx) * (dy)
    weight_br = (dx)       * (dy)
    
    TransformedImage[ti_y, ti_x, :] = ( weight_tl[:, None] * InitialImage[ii_tl_y,     ii_tl_x,     :] ) + \
                                      ( weight_tr[:, None] * InitialImage[ii_tl_y,     ii_tl_x + 1, :] ) + \
                                      ( weight_bl[:, None] * InitialImage[ii_tl_y + 1, ii_tl_x,     :] ) + \
                                      ( weight_br[:, None] * InitialImage[ii_tl_y + 1, ii_tl_x + 1, :] )


    # Getting x coorinate to remove black region from right and left in the transformed image
    min_x = min(ti_x)

    # Cropping out the black region from both sides (using symmetricity)
    TransformedImage = TransformedImage[:, min_x : -min_x, :]

    return TransformedImage, ti_x-min_x, ti_y

您只需调用该函数ProjectOntoCylinder并将图像传递给它,即可获得生成的图像和蒙版图像中白色像素的坐标。使用下面的代码调用此函数并获取蒙版图像。

# Applying Cylindrical projection on Image
Image_Cyl, mask_x, mask_y = ProjectOntoCylinder(Image)

# Getting Image Mask
Image_Mask = np.zeros(Image_Cyl.shape, dtype=np.uint8)
Image_Mask[mask_y, mask_x, :] = 255

以下是我的项目及其详细文档的链接以供参考:

第 1 部分: 源代码文档

第 2 部分: 源代码文档

于 2021-08-03T11:27:04.553 回答