模块'对象没有属性'drawMatches'opencv python
Posted
技术标签:
【中文标题】模块\'对象没有属性\'drawMatches\'opencv python【英文标题】:module' object has no attribute 'drawMatches' opencv python模块'对象没有属性'drawMatches'opencv python 【发布时间】:2013-12-14 01:43:55 【问题描述】:我只是在 OpenCV 中做一个特征检测的例子。此示例如下所示。它给了我以下错误
模块'对象没有属性'drawMatches'
我检查了 OpenCV 文档,但不确定为什么会出现此错误。有谁知道为什么?
import numpy as np
import cv2
import matplotlib.pyplot as plt
img1 = cv2.imread('box.png',0) # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage
# Initiate SIFT detector
orb = cv2.ORB()
# find the keypoints and descriptors with SIFT
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)
# create BFMatcher object
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Match descriptors.
matches = bf.match(des1,des2)
# Draw first 10 matches.
img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:10], flags=2)
plt.imshow(img3),plt.show()
错误:
Traceback (most recent call last):
File "match.py", line 22, in <module>
img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:10], flags=2)
AttributeError: 'module' object has no attribute 'drawMatches'
【问题讨论】:
How to visualize descriptor matching using opencv module in python 的可能重复项 哪些文档?并确保您使用的是正确的版本。 文档。 opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/… 这些不是官方的 OpenCV 文档!官方 OpenCV 文档可以在以下位置找到:docs.opencv.org 官方文档:不幸的是我看到了这个 OpenCV 3.0.0-dev 文档 【参考方案1】:drawMatches
函数不是 Python 接口的一部分。
正如您在docs 中看到的那样,它目前仅针对C++
定义。
文档摘录:
C++: void drawMatches(const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<DMatch>& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<char>& matchesMask=vector<char>(), int flags=DrawMatchesFlags::DEFAULT )
C++: void drawMatches(const Mat& img1, const vector<KeyPoint>& keypoints1, const Mat& img2, const vector<KeyPoint>& keypoints2, const vector<vector<DMatch>>& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector<vector<char>>& matchesMask=vector<vector<char> >(), int flags=DrawMatchesFlags::DEFAULT )
如果函数有 Python 接口,你会发现类似这样的内容:
Python: cv2.drawMatches(img1, keypoints1, [...])
编辑
实际上有一个commit 5 个月前介绍了这个功能。但是,它(还没有)在官方文档中。 确保您使用的是最新的 OpenCV 版本 (2.4.7)。 为了完整起见,OpenCV 3.0.0 的函数接口看起来像 this:
cv2.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2[, outImg[, matchColor[, singlePointColor[, matchesMask[, flags]]]]]) → outImg
【讨论】:
我在这里看到过opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/… 查看我的编辑,它是在 5 个月前添加的,但尚未出现在官方文档中。 如果我的回答有帮助,请考虑接受。要将答案标记为已接受,请单击答案旁边的复选标记以将其从空心切换为绿色。谢谢! 如果有提交,那不应该意味着这个功能可用吗?我无法将它与我的 py2.7.6_0 安装与 openCV 2.4.8_0 一起使用 不,它被提交到主干并不意味着它必须进入每个标签(如 2.4.8)。我不知道有什么计划,但我想这些更改将集成到 openCv 3.0 中。但是,如果您确实需要此功能,您可以自己下载源代码并构建 OpenCV 和 Python 绑定。【参考方案2】:我知道这个问题的答案是正确的,但如果您使用的是 OpenCV 2.4.8 而不是 3.0(-dev),解决方法可能是使用 opencv\sources\samples\python2\find_obj
import cv2
from find_obj import filter_matches,explore_match
img1 = cv2.imread('../c/box.png',0) # queryImage
img2 = cv2.imread('../c/box_in_scene.png',0) # trainImage
# Initiate SIFT detector
orb = cv2.ORB()
# find the keypoints and descriptors with SIFT
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)
# create BFMatcher object
bf = cv2.BFMatcher(cv2.NORM_HAMMING)#, crossCheck=True)
matches = bf.knnMatch(des1, trainDescriptors = des2, k = 2)
p1, p2, kp_pairs = filter_matches(kp1, kp2, matches)
explore_match('find_obj', img1,img2,kp_pairs)#cv2 shows image
cv2.waitKey()
cv2.destroyAllWindows()
这是输出图像:
【讨论】:
谢谢菜鸟,但是我自己为所需功能和其他功能添加了一个接口......仍然感谢【参考方案3】:我也迟到了,但我为 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
"""
# Create a new output image that concatenates the two images together
# (a.k.a) a montage
rows1 = img1.shape[0]
cols1 = img1.shape[1]
rows2 = img2.shape[0]
cols2 = img2.shape[1]
# Create the output image
# The rows of the output are the largest between the two images
# and the columns are simply the sum of the two together
# The intent is to make this a colour image, so make this 3 channels
out = np.zeros((max([rows1,rows2]),cols1+cols2,3), dtype='uint8')
# Place the first image to the left
out[:rows1,:cols1] = np.dstack([img1, img1, img1])
# Place the next image to the right of it
out[:rows2,cols1:] = np.dstack([img2, img2, img2])
# For each pair of points we have between both images
# draw circles, then connect a line between them
for mat in matches:
# Get the matching keypoints for each of the images
img1_idx = mat.queryIdx
img2_idx = mat.trainIdx
# x - columns
# y - rows
(x1,y1) = kp1[img1_idx].pt
(x2,y2) = kp2[img2_idx].pt
# Draw a small circle at both co-ordinates
# radius 4
# colour blue
# thickness = 1
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)
# Draw a line in between the two points
# thickness = 1
# colour blue
cv2.line(out, (int(x1),int(y1)), (int(x2)+cols1,int(y2)), (255,0,0), 1)
# Show the image
cv2.imshow('Matched Features', out)
cv2.waitKey(0)
cv2.destroyWindow('Matched Features')
# Also return the image if you'd like a copy
return out
为了说明这一点,这是我使用的两张图片:
我使用 OpenCV 的 ORB 检测器来检测关键点,并使用归一化的汉明距离作为相似度的距离度量,因为这是一个二进制描述符。因此:
import numpy as np
import cv2
img1 = cv2.imread('cameraman.png', 0) # Original image - ensure grayscale
img2 = cv2.imread('cameraman_rot55.png', 0) # Rotated image - ensure grayscale
# Create ORB detector with 1000 keypoints with a scaling pyramid factor
# of 1.2
orb = cv2.ORB(1000, 1.2)
# Detect keypoints of original image
(kp1,des1) = orb.detectAndCompute(img1, None)
# Detect keypoints of rotated image
(kp2,des2) = orb.detectAndCompute(img2, None)
# Create matcher
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Do matching
matches = bf.match(des1,des2)
# Sort the matches based on distance. Least distance
# is better
matches = sorted(matches, key=lambda val: val.distance)
# Show only the top 10 matches - also save a copy for use later
out = drawMatches(img1, kp1, img2, kp2, matches[:10])
这是我得到的图像:
与knnMatch
一起使用来自cv2.BFMatcher
我想说明上述代码仅在您假设匹配项出现在一维列表中时才有效。但是,例如,如果您决定使用 cv2.BFMatcher
中的 knnMatch
方法,则返回的是列表列表。具体来说,给定img1
中的描述符称为des1
和img2
中的描述符称为des2
,从knnMatch
返回的列表中的每个元素是另一个k
与des2
匹配的列表,它们是最接近des1
中的每个描述符。因此,knnMatch
输出的第一个元素是来自des2
的k
匹配列表,它们最接近在des1
中找到的第一个描述符。 knnMatch
输出的第二个元素是来自des2
的k
匹配列表,它们最接近在des1
中找到的第二个描述符,依此类推。
要充分利用knnMatch
,您必须将要匹配的邻居总数限制为k=2
。原因是您希望为每个可用的源点至少使用两个匹配点来验证匹配的质量,如果质量足够好,您将希望使用这些来绘制匹配并将它们显示在屏幕。您可以使用一个非常简单的比率测试(信用转到David Lowe)来确保对于一个点,我们看到与最佳点匹配的距离/相异度远小于与次佳相匹配的距离/相异度观点。我们可以通过找到最佳匹配点与第二最佳匹配点的距离的比率来捕捉这一点。该比率应该很小,以说明一个点与其最佳匹配点是明确的。如果比率接近 1,这意味着两个匹配都同样“好”,因此不明确,因此我们不应该包括这些。我们可以将其视为异常值拒绝技术。因此,要将 knnMatch
返回的内容转换为我上面编写的代码所需的内容,遍历匹配项,使用上述比率测试并检查它是否通过。如果是,则将第一个匹配的关键点添加到新列表中。
假设您像在声明 BFMatcher
实例之前一样创建了所有变量,那么您现在将执行此操作以调整 knnMatch
方法以使用 drawMatches
:
# Create matcher
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Perform KNN matching
matches = bf.knnMatch(des1, des2, k=2)
# Apply ratio test
good = []
for m,n in matches:
if m.distance / n.distance < 0.75: # Or you can do m.distance < 0.75 * n.distance
# Add the match for point m to the best
good.append(m)
# Or do a list comprehension
#good = [m for (m,n) in matches if m.distance < 0.75*n.distance]
# Now perform drawMatches
out = drawMatches(img1, kp1, img2, kp2, good)
当您遍历 matches
列表时,m
和 n
应该是来自 des1
的点与其最佳匹配 (m
) 与其第二最佳匹配 (n
) 之间的匹配都来自des2
。如果我们看到该比率很小,我们会将两点之间的最佳匹配 (m
) 添加到最终列表中。我所拥有的比率 0.75 是一个需要调整的参数,所以如果你没有得到好的结果,请尝试这个直到你做到为止。但是,0.7 到 0.8 之间的值是一个好的开始。
我想将上述修改归因于用户@ryanmeasel,找到这些修改的答案在他的帖子中:OpenCV Python : No drawMatchesknn function。
【讨论】:
感谢...返回“out”非常有用,因为我在小型嵌入式设备上运行并且需要 10 小时来重新编译 opencv。让我完成工作并上床睡觉:) @wentbackward - 你好!不客气!很高兴返回out
。我只是想显示实际的图像匹配,但返回 out
非常适合保存结果以供以后显示:) 一切顺利!
@Aphire - 啊谢谢 :) 很高兴这段代码帮助了你!
@rayryeng 嗨,你有什么想法,为什么我会收到这个错误:ValueError: could not broadcast input array from shape (347,550,9) into shape (347,550,3)
?此错误发生在您将第一张图片放在左侧的行中。
我不能再编辑我的问题了,但问题是图像没有灰度化:)以上是关于模块'对象没有属性'drawMatches'opencv python的主要内容,如果未能解决你的问题,请参考以下文章