目标
在本章中,
- 我们将混合使用来自 calib3d 模块的特征匹配和 findHomography,在复杂图像中查找已知对象。
基础
我们在上一节做了什么?我们使用了一个 queryImage,在其中找到了一些特征点,我们取了另一个 trainImage,也在该图像中找到了特征,并从中找到了最佳匹配项。简而言之,我们在另一幅杂乱的图像中找到了某个对象的部分位置。此信息足以在 trainImage 中精确地找到该对象。
为此,我们可以使用 calib3d 模块的一个函数,即 cv.findHomography()。如果我们从两幅图像中传递一组点,它将找到该对象的透视变换。然后我们可以使用 cv.perspectiveTransform() 查找该对象。它至少需要四个正确点才能找到该变换。
我们已经发现,在匹配过程中可能会出现一些可能影响结果的错误。为了解决此问题,该算法使用 RANSAC 或 LEAST_MEDIAN(可通过标志决定)。因此,提供正确估计值的好匹配称为内点,其余称为外点。 cv.findHomography() 返回一个指定内点和外点的掩码。
所以让我们动手吧!
代码
首先,像往常一样,我们在图像中找到 SIFT 特征并应用比率测试以找到最佳匹配项。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
MIN_MATCH_COUNT = 10
img1 =
cv.imread(
'box.png', cv.IMREAD_GRAYSCALE)
img2 =
cv.imread(
'box_in_scene.png', cv.IMREAD_GRAYSCALE)
sift = cv.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
FLANN_INDEX_KDTREE = 1
index_params = 字典(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = 字典(checks = 50)
matches = flann.knnMatch(des1,des2,k=2)
good = []
对于 matches 中的 m、n
如果 m.distance < 0.7*n.distance
good.append(m)
基于 Flann 的描述符匹配器。
定义 features2d.hpp:1294
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR)
从文件加载图像。
现在,我们设置一个条件,即至少有 10 个匹配项(由 MIN_MATCH_COUNT 定义)存在来查找该对象。否则,只需显示一条消息,说明匹配项不够多。
如果找到足够的匹配项,我们将提取两张图像中匹配关键点的点位。它们被用来找到透视变换。一旦我们得到这个 3x3 变换矩阵,我们用它将 queryImage 的角变换到 trainImage 中的对应点。然后我们绘制它。
如果 len(good)>MIN_MATCH_COUNT
src_pts = np.float32([ kp1[m.queryIdx].pt 对于 good 中的 m ]).reshape(-1,1,2)
dst_pts = np.float32([ kp2[m.trainIdx].pt 对于 good 中的 m ]).reshape(-1,1,2)
matchesMask = mask.ravel().tolist()
h,w = img1.shape
pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
img2 =
cv.polylines(img2,[np.int32(dst)],
True,255,3, cv.LINE_AA)
else:
print( "匹配项不够 - {}/{}".format(len(good), MIN_MATCH_COUNT) )
matchesMask = None
Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray(), const int maxIters=2000, const double confidence=0.995)
查找两个平面之间的透视线性变换。
void perspectiveTransform(InputArray src, OutputArray dst, InputArray m)
对向量执行透视矩阵变换。
void polylines(InputOutputArray img, InputArrayOfArrays pts, bool isClosed, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
绘制几条多边形曲线。
最后,我们绘制内点(如果成功找到物体)或匹配关键点(如果失败)。
draw_params = 字典(matchColor = (0,255,0),
singlePointColor = None,
matchesMask = matchesMask,
flags = 2)
plt.imshow(img3, 'gray'),plt.show()
void drawMatches(InputArray img1, const std::vector< KeyPoint > &keypoints1, InputArray img2, const std::vector< KeyPoint > &keypoints2, const std::vector< DMatch > &matches1to2, InputOutputArray outImg, const Scalar &matchColor=Scalar::all(-1), const Scalar &singlePointColor=Scalar::all(-1), const std::vector< char > &matchesMask=std::vector< char >(), DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT)
绘制来自两张图像的关键点的找到的匹配项。
请参阅以下结果。杂乱的图像中,对象标记为白色
图像
其他资源
练习