Python OpenCV ORB 图像对齐的掩码问题

4
我正在尝试实现Python(3.7)OpenCV(3.4.3)ORB图像对齐。我通常使用ImageMagick进行大部分处理。但是我需要进行一些图像对齐,并尝试使用Python OpenCV ORB。我的脚本基于Satya Mallick的Learn OpenCV教程中的一个脚本,链接如下:https://www.learnopencv.com/image-alignment-feature-based-using-opencv-c-python/然而,我正在尝试修改它以使用刚性对齐而不是透视同源,并使用掩码过滤点来限制y值之间的差异,因为图像已经几乎对齐。 掩码方法来自最后一个示例中的FLANN对齐代码,链接如下:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_feature2d/py_matcher/py_matcher.html
我的脚本很好用,如果我去掉matchesMask,那么它就可以提供点过滤。(我有另外两个工作脚本,一个类似,但只过滤点并忽略掩码,另一个基于ECC算法。) 然而,我想知道为什么我的下面的代码不起作用。 也许我的掩码结构在当前版本的Python Opencv中是不正确的?
我得到的错误是:
Traceback (most recent call last):
  File "warp_orb_rigid2_filter.py", line 92, in <module>
    imReg, m = alignImages(im, imReference)
  File "warp_orb_rigid2_filter.py", line 62, in alignImages
    imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None, **draw_params)
SystemError: <built-in function drawMatches> returned NULL without setting an error

这是我的代码。第一个箭头显示了遮罩创建的位置。第二个箭头显示了我必须删除的行,以使脚本正常工作。但这样它会忽略我对点的过滤。
#!/bin/python3.7

import cv2
import numpy as np


MAX_FEATURES = 500
GOOD_MATCH_PERCENT = 0.15


def alignImages(im1, im2):

  # Convert images to grayscale
  im1Gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
  im2Gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)

  # Detect ORB features and compute descriptors.
  orb = cv2.ORB_create(MAX_FEATURES)
  keypoints1, descriptors1 = orb.detectAndCompute(im1Gray, None)
  keypoints2, descriptors2 = orb.detectAndCompute(im2Gray, None)

  # Match features.
  matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
  matches = matcher.match(descriptors1, descriptors2, None)

  # Sort matches by score
  matches.sort(key=lambda x: x.distance, reverse=False)

  # Remove not so good matches
  numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
  matches = matches[:numGoodMatches]

  # Extract location of good matches and filter by diffy
  points1 = np.zeros((len(matches), 2), dtype=np.float32)
  points2 = np.zeros((len(matches), 2), dtype=np.float32)

  for i, match in enumerate(matches):
    points1[i, :] = keypoints1[match.queryIdx].pt
    points2[i, :] = keypoints2[match.trainIdx].pt

  # initialize empty arrays for newpoints1 and newpoints2 and mask
  newpoints1 = np.empty(shape=[0, 2])
  newpoints2 = np.empty(shape=[0, 2])
  matches_Mask = [0] * len(matches)

  # filter points by using mask    
  for i in range(len(matches)):
      pt1 = points1[i]
      pt2 = points2[i]
      pt1x, pt1y = zip(*[pt1])
      pt2x, pt2y = zip(*[pt2])
      diffy = np.float32( np.float32(pt2y) - np.float32(pt1y) )
      print(diffy)
      if abs(diffy) < 10.0:
        newpoints1 = np.append(newpoints1, [pt1], axis=0)
        newpoints2 = np.append(newpoints2, [pt2], axis=0)
        matches_Mask[i]=[1,0]  #<--- mask created
  print(matches_Mask)

  draw_params = dict(matchColor = (255,0,),
    singlePointColor = (255,255,0),
    matchesMask = matches_Mask, #<---- remove mask here
    flags = 0)

  # Draw top matches
  imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None, **draw_params)
  cv2.imwrite("/Users/fred/desktop/lena_matches.png", imMatches)


  # Find Affine Transformation
  # true means full affine, false means rigid (SRT)
  m = cv2.estimateRigidTransform(newpoints1,newpoints2,False)

  # Use affine transform to warp im1 to match im2
  height, width, channels = im2.shape
  im1Reg = cv2.warpAffine(im1, m, (width, height))

  return im1Reg, m


if __name__ == '__main__':

  # Read reference image
  refFilename = "/Users/fred/desktop/lena.png"
  print("Reading reference image : ", refFilename)
  imReference = cv2.imread(refFilename, cv2.IMREAD_COLOR)

  # Read image to be aligned
  imFilename = "/Users/fred/desktop/lena_r1.png"
  print("Reading image to align : ", imFilename);  
  im = cv2.imread(imFilename, cv2.IMREAD_COLOR)

  print("Aligning images ...")
  # Registered image will be stored in imReg. 
  # The estimated transform will be stored in m. 
  imReg, m = alignImages(im, imReference)

  # Write aligned image to disk. 
  outFilename = "/Users/fred/desktop/lena_r1_aligned.jpg"
  print("Saving aligned image : ", outFilename); 
  cv2.imwrite(outFilename, imReg)

  # Print estimated homography
  print("Estimated Affine Transform : \n",  m)

这是我的两张图片:lena和旋转1度的lena。请注意,这些不是我的实际图片。这些图片没有diffy值大于10,但我的实际图片有。

enter image description here

enter image description here

我正在尝试对旋转图像进行对齐和扭曲,以匹配原始的lena图像。

1个回答

2
你创建掩码的方式不正确。它只需要是一个列表,其中包含单个数字,每个数字告诉你是否要使用该特定特征匹配。
因此,请将这行代码替换为:
matches_Mask = [[0,0] for i in range(len(matches))]

使用以下内容:

matches_Mask = [0] * len(matches)

... so:

# matches_Mask = [[0,0] for i in range(len(matches))]
matches_Mask = [0] * len(matches)

这将创建一个0的列表,其长度与匹配项数量相同。最后,您需要使用单个值将写入掩码进行更改: "最初的回答"
  if abs(diffy) < 10.0:
    #matches_Mask[i]=[1,0]  #<--- mask created
    matches_Mask[i] = 1

I finally get this:

Estimated Affine Transform :
 [[ 1.00001187  0.01598318 -5.05963793]
  [-0.01598318  1.00001187 -0.86121051]]

请注意,掩模的格式取决于您使用的匹配器。在这种情况下,您使用暴力匹配,因此掩模需要按照我刚才描述的格式进行设置。
例如,如果您使用了FLANN的knnMatch,则它将是一个嵌套的列表,每个元素都是长度为k的列表。例如,如果您有k=3和五个关键点,则它将是一个五个元素长的列表,每个元素都是一个三个元素的列表。子列表中的每个元素都说明了要用于绘图的匹配。

@rayryeng 非常感谢。但请解释一下为什么掩码不是[1,0],就像其他参考文献中一样。这是由于我的DescriptorMatcher_create()和其他参考文献中flann.knnMatch()的不同掩码结构引起的吗?是否有一些参考资料可以解释不同匹配器的掩码结构?此外,在上面的代码中,我使用了两个简单的图像,而不是我无法在此处展示的实际图像,但它们确实具有大于10的diffy值。我将编辑我的帖子以更好地解释这一点。 - fmw42
是的,就是这样。暴力算法和kNN有不同的结构。使用kNN需要使用您的原始实现。我也没有关于掩码结构的参考...这来自经验。 - rayryeng
我也不知道你在使用FLANN。你的代码正在使用暴力匹配,因此掩码只是一个单一的列表。对于kNN匹配,如果您使用“k = 2”,那么它将是一个嵌套的列表,每个元素都是一个长度为“k”的列表。例如,如果您有“k = 5”和三个匹配项,则它将是一个三个元素长的列表,每个元素都是一个5元素列表。子列表中的每个元素都说明了要用于绘制的匹配项。 - rayryeng
1
@rayryeng。我实际上并没有使用FLANN,而只是试图模仿在FLANN示例中执行的掩码结构。因此,匹配器的掩码结构会发生变化(可能会和FLANN不同)。感谢您提供的澄清。我按照您的建议修改了我的脚本,现在即使对于我的实际图像也可以正常工作。由于经过筛选处理,有13个点被排除了,但现在这个掩码可以很好地处理它。非常感谢您这位初学Python/OpenCV的新手提供的帮助和专业知识。 - fmw42
@fmw42 我明白了。好的,如果你下次决定使用FLANN,你知道如何构建掩码了!很高兴能帮到你,请告诉我是否还有任何问题或需要更多的帮助! - rayryeng

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