我也有点晚了,但是我在Mac OS X上安装了OpenCV 2.4.9,而我的发行版中没有 drawMatches
函数。我也尝试了第二种方法,使用 find_obj
,但对我也不起作用。因此,我决定自己编写实现它的代码,尽力模仿 drawMatches
,以下是我的实现。
我提供了自己的图像,其中一个是相机人的图像,另一个是同一图像逆时针旋转55度后的图像。
我编写的基本原理是分配一个输出RGB图像,其中行数是两个图像中最大的那个,以容纳将两个图像放置在输出图像中,列则是两列之和。请注意,我假设两个图像都是灰度的。
我将每个图像放置在其对应的位置,然后运行所有匹配关键点的循环。我提取了两个图像之间匹配的关键点,然后提取它们的 (x,y)
坐标。我在检测到的每个位置绘制圆,并画一条连接这些圆的直线。
请记住,第二个图像中检测到的关键点是相对于它自己的坐标系的。如果您想将其放置在最终输出图像中,则需要通过第一个图像的列数来偏移列坐标,以便列坐标相对于输出图像的坐标系。
不多说了:
import numpy as np
import cv2
def drawMatches(img1, kp1, img2, kp2, matches):
"""
My own implementation of cv2.drawMatches as OpenCV 2.4.9
does not have this function available but it's supported in
OpenCV 3.0.0
This function takes in two images with their associated
keypoints, as well as a list of DMatch data structure (matches)
that contains which keypoints matched in which images.
An image will be produced where a montage is shown with
the first image followed by the second image beside it.
Keypoints are delineated with circles, while lines are connected
between matching keypoints.
img1,img2 - Grayscale images
kp1,kp2 - Detected list of keypoints through any of the OpenCV keypoint
detection algorithms
matches - A list of matches of corresponding keypoints through any
OpenCV keypoint matching algorithm
"""
rows1 = img1.shape[0]
cols1 = img1.shape[1]
rows2 = img2.shape[0]
cols2 = img2.shape[1]
out = np.zeros((max([rows1,rows2]),cols1+cols2,3), dtype='uint8')
out[:rows1,:cols1] = np.dstack([img1, img1, img1])
out[:rows2,cols1:] = np.dstack([img2, img2, img2])
for mat in matches:
img1_idx = mat.queryIdx
img2_idx = mat.trainIdx
(x1,y1) = kp1[img1_idx].pt
(x2,y2) = kp2[img2_idx].pt
cv2.circle(out, (int(x1),int(y1)), 4, (255, 0, 0), 1)
cv2.circle(out, (int(x2)+cols1,int(y2)), 4, (255, 0, 0), 1)
cv2.line(out, (int(x1),int(y1)), (int(x2)+cols1,int(y2)), (255,0,0), 1)
cv2.imshow('Matched Features', out)
cv2.waitKey(0)
cv2.destroyWindow('Matched Features')
return out
为了说明这个方法的可行性,这里是我使用的两张图片:
我使用了OpenCV的ORB检测器来检测关键点,并且使用标准化的汉明距离作为相似性的距离度量,因为这是一种二进制描述符。 因此:
import numpy as np
import cv2
img1 = cv2.imread('cameraman.png', 0)
img2 = cv2.imread('cameraman_rot55.png', 0)
orb = cv2.ORB(1000, 1.2)
(kp1,des1) = orb.detectAndCompute(img1, None)
(kp2,des2) = orb.detectAndCompute(img2, None)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1,des2)
matches = sorted(matches, key=lambda val: val.distance)
out = drawMatches(img1, kp1, img2, kp2, matches[:10])
这是我得到的图像:
与 cv2.BFMatcher
中的 knnMatch
一起使用
我想指出上面的代码仅在假定匹配项出现在 1D 列表中时才起作用。但是,如果您决定使用来自 cv2.BFMatcher
的 knnMatch
方法,那么返回的将是一个列表嵌套列表的形式。具体而言,在给定名为 des1
的 img1
描述符和名为 des2
的 img2
描述符的情况下,从 knnMatch
返回的列表中的每个元素都是另一个列表,其中包含了与 des1
中各描述符最相似的 k
个来自 des2
的匹配项。因此,knnMatch
输出的第一个元素是来自 des2
的 k
个最接近 des1
中找到的第一个描述符的匹配项列表。第二个元素是来自 des2
的 k
个最接近 des1
中找到的第二个描述符的匹配项列表,以此类推。
为了最大程度地利用 knnMatch
的优势,您必须将要匹配的邻居的总数限制为k=2
。原因是因为您想使用至少两个匹配点来验证每个可用源点的质量,并且如果质量足够好,您将希望使用这些点来绘制匹配项并在屏幕上显示它们。您可以使用一个非常简单的比率测试(由David Lowe创作)来确保对于一个点,我们看到匹配到最佳点的距离/不相似性远小于匹配到第二佳点的距离/不相似性。我们可以通过计算最佳匹配点与次佳匹配点之间距离的比率来捕获这一点。这个比率应该很小,以说明点到其最佳匹配点是明确的。如果比率接近 1,则意味着两个匹配项同样“好”,因此是不确定的,因此我们不应将其包括在内。我们可以将此视为异常值拒绝技术。因此,要将从 knnMatch
返回的内容转换为我上面编写的代码所需的内容,请遍历匹配项,使用上述比率测试并检查是否通过。如果通过,请将第一个匹配的关键点添加到新列表中。
假设您像之前一样声明了所有变量以创建 BFMatcher
实例,则现在要做的是调整 knnMatch
方法以适用于使用 drawMatches
:
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.knnMatch(des1, des2, k=2)
good = []
for m,n in matches:
if m.distance / n.distance < 0.75:
good.append(m)
out = drawMatches(img1, kp1, img2, kp2, good)
当你遍历
matches
列表时,
m
和
n
应该是来自
des1
中某个点和其在
des2
中的最佳匹配(
m
)及次佳匹配(
n
)。如果发现比值很小,就会将这两个点之间的最佳匹配(
m
)添加到最终列表中。我所用的比值为0.75,这是需要调整的参数,如果结果不佳,请尝试调整该参数值。然而,0.7到0.8之间的值是一个好的起点。
以上修改要归功于用户
@ryanmeasel,并且这些修改的答案可以在他的帖子中找到:
OpenCV Python : No drawMatchesknn function。