问题描述
将 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,cv2.THRESH_BINARY)
kp2,descriptors2 = detector.detectAndCompute(gray2,mask2)
keypoints1Im = cv2.drawKeypoints(image1,kp1,outimage = cv2.DRAW_MATCHES_FLAGS_DEFAULT,color=(0,255))
keypoints2Im = cv2.drawKeypoints(image2,kp2,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,gray1,matches)
#Aligning the images
src_pts = np.float32([ kp2[m.queryIdx].pt for m in matches ]).reshape(-1,2)
dst_pts = np.float32([ kp1[m.trainIdx].pt for m in matches ]).reshape(-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,0]]).reshape(-1,2)
pts2 = np.float32([[0,h2],[w2,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,t[0]],t[1]],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,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 张图像开始拼接,那么拼接也很好,但是当我从第一张图像开始拼接时会出现问题。我无法理解这个问题。
拼接第23张图片后的结果:
拼接第24张图片后的结果:
拼接第 25 张图片后的结果如上,出错了。
奇怪的观察:如果我用相同的代码分别缝合 23、24、25 个图像,它会缝合。如果我在 23 到 97 之后缝合图像,则会缝合。但是不知何故,如果我从第 1 个图像开始缝合,它会在缝合第 25 个图像时中断。我不明白为什么会发生这种情况。
我尝试了不同的组合,如不同的关键点检测、提取方法、匹配方法、不同的单应性计算、不同的变形代码,但这些组合不起作用。步骤组合代码中缺少某些内容或错误。我无法弄清楚。
抱歉问了这么长的问题。由于我对此完全陌生,因此我无法解释并正确理解这些内容。感谢您的帮助和指导。
使用不同的代码(在拼接之间给出黑线),如果我拼接了 97 个图像,那么第 25 个在拼接和拼接中上升,如下所示(右角点):
解决方法
首先,我无法重现您的问题并解决它,因为图像太大,我的系统无法处理。然而,我在我的全景拼接项目中遇到了同样的问题,所以我分享了它背后的原因和我解决问题的方法。希望这对您也有帮助。
当我像你一样将 4 张图像拼接在一起时,我的问题是这样的。
如您所见,第 4 幅图像严重失真,这是绝对不能发生的。同样的事情也发生在你身上,但程度更高。
现在,这是我在一些图像预处理后拼接 8 张图像时的输出。
对输入图像进行一些预处理后,我能够完美地将 8 张图像拼接在一起,没有任何失真。
要了解这种失真背后的确切原因,请观看 Joseph Redmon 在 50:26 - 1:07:23 之间的 this video。
按照视频中的建议,我们首先必须将图像投影到圆柱体上,然后展开它们,然后将这些展开的图像拼接在一起。
下面是初始输入图像(左)和投影并展开到圆柱体后的图像(右)。
对于您的问题,当您使用卫星图像时,我猜投影到球体上会比圆柱体效果更好,但是您必须尝试一下。
在我的代码下方分享用于将图像投影到圆柱体上并展开它以供参考。其背后使用的数学与视频中给出的相同。
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[:,ii_tl_x + 1,:] ) + \
( weight_bl[:,None] * InitialImage[ii_tl_y + 1,:] ) + \
( weight_br[:,:] )
# 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,:] = 255
以下是我的项目及其详细文档的链接以供参考:
第 1 部分: Source Code, Documentation
第 2 部分: Source Code, Documentation