目标
本章节中,
- 我们将结合特征匹配和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)
绘制从两张图像中找到的关键点的匹配项。
请参见下面的结果。物体在杂乱的图像中用白色标记
图像