Brute-Force 蛮力匹配
1、导入需要的库
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
2、定义绘图函数
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
3、读入需要进行匹配的两张图片
img1 = cv2.imread('box.png', 0)
img2 = cv2.imread('box_in_scene.png', 0)
cv_show('img1',img1)
cv_show('img2',img2)
展示图片:
4、实例化SIFT函数
sift = cv2.xfeatures2d.SIFT_create()
5、检测特征点并计算特征
kp1, des1 = sift.detectAndCompute(img1, None)
print(np.array(kp1).shape)
print(np.array(des1).shape)
kp2, des2 = sift.detectAndCompute(img2, None)
print(np.array(kp2).shape)
print(np.array(des2).shape)
可以得到结果:img1中有603个特征点,img2中有969个特征点,因为从img1和img2本身的图像来看,img2中是包含img1的,所以进行特征匹配的时候只会匹配img1和img2共有的603个特征点。
ps:当然,这里也可以分开来做,即先检测特征点再计算特征。即:
kp1 = sift.detect(img1, None)
kp2 = sift.detect(img2, None)
kp1, des1 = sift.compute(img1, kp1)
kp2, des2 = sift.compute(img2, kp2)
6、进行BF匹配
此时,情况应分成进行1对1的匹配还是k对最佳匹配。
1对1匹配
创建BF匹配器对象
函数介绍:
cv2.BFMatcher(normType=cv2.NORM_L2, crossCheck=True)
传入参数:
- normType:指定要使用的距离量度。默认是cv2.NORM_L2。适用于SIFT和SURF特征。对于二进制字符串的描述子,比如ORB,BRIEF,BRISK等,应该用cv2.NORM_HAMMING。使用Hamming距离度量,如果ORB使用VTA_K == 3或者4,应该用cv2.NORM_HAMMING2。
- crossCheck:表示两个特征点要互相匹配,例如A中的第i个特征点与B中的第j个特征点最近的,并且B中的第j个特征点到A中的第i个特征点也是。当BF匹配器对象创建以后,两个重要的方法是BFMatcher.match()和BFMatcher.knnMatch()。第一个返回最匹配的,crossCheck=True,第二个方法返回k个最匹配的,k由用户指定,此时crossCheck=False。
bf = cv2.BFMatcher(normType=cv2.NORM_L2, crossCheck=True)
进行匹配操作
函数介绍:
bf.match(des1, des2)
输出:
DMatch类型的数据结构。
那么这个这个DMatch数据结构究竟是什么呢?
它包含三个非常重要的数据分别是queryIdx,trainIdx,distance。
- queryIdx:此匹配对应的测试图像的特征描述子索引(第几个特征点描述符)。即DMatch.queryIdx表示一个测试图像中的特征点。对于一个特征点来说,它本身也具有以下属性:.pt:关键点坐标,.angle:表示关键点方向,.response表示响应强度,.size:表示该点的直径大小。
- trainIdx:此匹配对应的训练(模板)图像的特征描述子索引(第几个特征点描述符)。即DMatch.trainIdx表示一个训练(模板)图像中的特征点。同样地,对于一个特征点来说,它本身也具有以下属性:.pt:关键点坐标,.angle:表示关键点方向,.response表示响应强度,.size:表示该点的直径大小。
- distance:代表这对匹配的特征点描述符的欧式距离,数值越小也就说明俩个特征点越相近。即DMatch.distance表示一个距离。
matches = bf.match(des1, des2)
找出最匹配的十个特征点并将它们用线连起来
函数介绍:
cv2.drawMatches(imageA, kpsA, imageB, kpsB, matches[:10], None, flags=2)
传入参数:
- imageA和imageB表示图片。
- kpsA和kpsB表示关键点。
- matches表示进过cv2.BFMatcher获得的匹配的索引值,也有距离。
- flags表示有几个图像。
matches = sorted(matches, key=lambda x: x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None,flags=2)
cv_show('img3',img3)
得到结果:
k对最佳匹配
k对最佳匹配是指,每一个特征点在另一张图片上都会有k个最匹配的点存在,例如k=2,它会给每个特征点画两根匹配线。
实现方法与1对1匹配大同小异。
创建BF匹配器对象
注意,此时,crossCheck=False(默认)
bf = cv2.BFMatcher()
进行匹配操作
函数介绍:
bf.knnMatch(des1, des2, k=2)
输出:
Knnmatch与match的返回值类型一样,都是DMatch类型的数据结构,只不过一组返回的k(此处=2)个DMatch类型。
matches = bf.knnMatch(des1, des2, k=2)
找出匹配的特征点并将它们用线连起来
在这里,每一个特征点(设为A)都有最匹配的另外两个特征点在另一张图上,设为B和C。其中AB的距离最短,AC的距离次短。
那么规定,如果AB间的距离小于AC间距离的0.75倍,则认为AB的匹配度远远大于A与其他任何点的匹配度,则将AB两点连起来。
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append([m])
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)
cv_show('img3',img3)
得到结果: