首先在第一幅图像中选取一个关键点,然后依次与第二幅图像的每个关键点进行(描述符)距离测试,最后返回距离最近的关键点。
代码速记:
参数解释:
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)#返回最佳匹配
matches = bf.knnMatch(des1, des2, k=2)
#每个关键点返回k 个最佳匹配(降序排列之后取前k 个),其中k 是由用户设定的。
#如果除了匹配之外还要做其他事情的话可能会用上(比如进行比值测试)。
bf:match(des1,des2)返回值是一个DMatch 对象列表。这个DMatch 对象具有下列属性:
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2) # 前10个匹配
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
用来绘制匹配的点。它会将这两幅图像先水平排列,然后在最佳匹配的点之间绘制直线(从原图像到目标图像)。如果前面使用的是BFMatcher.knnMatch(),现在我们可以使用函数cv2.drawMatchsKnn为每个关键点和它的k 个最佳匹配点绘制匹配线。如果k 等于2,就会为每个关键点绘制两条最佳匹配直线。如果我们要选择性绘制话就要给函数传入一个掩模。
实战:
我们有一个查询图像和一个目标图像。我们要使用特征匹配的方法在目标图像中寻找查询图像的位置。
def bf_orb(self):
img1 = cv2.imread('../images/box.png', 0) # queryImage
img2 = cv2.imread('../images/box_in_scene.png', 0) # trainImage
#【1】初始ORB特征检测器
orb = cv2.ORB_create()
#【2】用ORB找到两幅图像的关键点和描述符
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
#【3】创建BFMatcher对象
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
#【4】匹配两幅图像的描述符
matches = bf.match(des1, des2)
#【5】根据描述符之间的距离来排序
matches = sorted(matches, key=lambda x: x.distance)
#【6】画出前10匹配的特征点
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2) # 前10个匹配
plt.imshow(img3), plt.xticks([]), plt.yticks([]),plt.show()
代码速记:
实战:
def bf_sift(self):
img1 = cv2.imread('../images/box.png', 0)# queryImage
img2 = cv2.imread('../images/box_in_scene.png', 0) # trainImage
#【1】初始化SIFT对象
sift = cv2.xfeatures2d.SIFT_create()
#【2】用SIFT找到关键点和描述符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
#【3】创建BFMatcher对象,使用默认参数
bf = cv2.BFMatcher()
#【4】用knn方法获得k 对最佳匹配
matches = bf.knnMatch(des1, des2, k=2)
#【5】比值测试
# 首先获取与 A距离最近的点 B (最近)和 C (次近),
# 只有当 B/C 小于阀值时(0.75)才被认为是匹配,
# 因为假设匹配是一一对应的,真正的匹配的理想距离为0
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append([m])
#【6】画出匹配点:参数是match list
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
plt.imshow(img3), plt.xticks([]), plt.yticks([]),plt.show()
FLANN 是快速最近邻搜索包(Fast_Library_for_Approximate_Nearest_Neighbors)的简称。它是一个对大数据集和高维特征进行最近邻搜索的算法的集合,而且这些算法都已经被优化过了。在面对大数据集时它的效果要好于BFMatcher。
代码速记:
参数解释:
flann = cv2.FlannBasedMatcher(index_params, search_params)
我们需要传入两个字典作为参数。这两个用来确定要使用的算法和其他相关参数等。第一个是IndexParams。各种不同算法的信息可以在FLANN 文档中找到。这里我们总结一下,对于SIFT 和SURF 等,我们可以传入的参数是:
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)]
第二个字典是SearchParams。用它来指定递归遍历的次数。值越高结果越准确,但是消耗的时间也越多。如果你想修改这个值,传入参数:
search_params = dict(checks=50)
实战:
def flann(self):
img1 = cv2.imread('../images/box.png', 0) # queryImage
img2 = cv2.imread('../images/box_in_scene.png', 0) # trainImage
#【1】特征检测、特征描述
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
#【2】准备FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50) # or pass empty dictionary
#【3】FLANN匹配
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
# Need to draw only good matches, so create a mask
matchesMask = [[0, 0] for i in range(len(matches))]
# 比值测试
for i, (m, n) in enumerate(matches):
if m.distance < 0.7 * n.distance:
matchesMask[i] = [1, 0]
#【4】画出匹配点
draw_params = dict(matchColor=(0, 255, 0),
singlePointColor=(255, 0, 0),
matchesMask=matchesMask,
flags=0)
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None, **draw_params)
plt.imshow(img3), plt.xticks([]), plt.yticks([]),plt.show()
代码速记:
参数解释:
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
实战:
def homo(self):
#【1】先在图像中来找到SIFT 特征点,然后再使用比值测试找到最佳匹配。
img1 = cv2.imread('../images/box.png', 0) # queryImage
img2 = cv2.imread('../images/box_in_scene.png', 0) # trainImage
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good.append(m)
#【2】只有存在10 个以上匹配时才去查找目标,否则显示警告消息:“现在匹配不足!”
MIN_MATCH_COUNT = 10
if len(good) > MIN_MATCH_COUNT:
#【3】提取两幅图像中匹配点的坐标
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)
#【4】传入到函数中计算透视变换矩阵
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
matchesMask = mask.ravel().tolist()
h, w = img1.shape# 获得原图像的高和宽
#【5】使用得到的变换矩阵对原图像的四个角进行变换,获得在目标图像上对应的坐标。
pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, M)
#【6】在train image中画出变换后的白色对象框
img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
print("Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT))
matchesMask = None
#【7】绘制inliers(如果能成功的找到目标图像的话)或者匹配的关键点(如果失败)。
draw_params = dict(matchColor=(0, 255, 0), # draw matches in green color
singlePointColor=None,
matchesMask=matchesMask, # draw only inliers
flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)
plt.imshow(img3), plt.xticks([]), plt.yticks([]),plt.show()