目标
在本章中,
- 我们将混合使用特征匹配和 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 = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks = 50)
matches = flann.knnMatch(des1,des2,k=2)
good = []
for m,n in matches
if 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_BGR)
从文件加载图像。
现在我们设置一个条件,至少要有 10 个匹配项(由 MIN_MATCH_COUNT 定义)才能找到对象。否则,只需显示一条消息,说明没有足够的匹配项。
如果找到足够的匹配项,我们提取两个图像中匹配关键点的位置。它们被传递以找到透视变换。一旦我们得到这个 3x3 变换矩阵,我们就使用它将 queryImage 的角转换为 trainImage 中的对应点。然后我们绘制它。
if len(good)>MIN_MATCH_COUNT
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).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 = dict(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)
绘制两幅图像中找到的关键点匹配。
请参阅下面的结果。对象在杂乱的图像中以白色标记
image