计算机视觉Opencv实验合集——实验二:特征匹配

 在实验二当中

  • 我们将看到如何将一个图像中的特征点与其他图像进行匹配。
  • 我们将在 OpenCV 中使用蛮力(Brute-Force)匹配和 FLANN 匹配

特征匹配主要是基于两种相似度较高的图片,通过Opencv里面提供的特征匹配方法来进行特征点之间的匹配和映射

特征点由关键点和描述子两部分组成。例如:在一张图像中计算SIFT特征点时,是指提取SIFT关键点,并计算SIFT描述子两件事。关键点是指特征点在图像里的位置,有些特征点还具有方向、大小等信息。描述子是指一个向量,描述该关键点周围像素的信息,按照“外观相似特征应该有相似的描述子”的原则设计

SIFT(尺度不变特征)做为最经典的特征提取算法,充分考虑图像变换过程中出现的光照、尺度、旋转等变化,但计算量较大,实时性不好。
FAST关键点(没有描述子)考虑适当降低精度和鲁棒性,以提升计算的速度,属于特别快的一种特征点。
ORB特征目前看来非常具有代表性的实时图像特征,它改进了FAST检测子不具有方向性问题,并采用速度极快的二进制描述子BRIEF;因此,ORB在保持特征子具有旋转、尺度不变性的同时,在速度方面也有了很大的提升。

本次实验主要展示如何通过SIFT进行特征匹配

SIFT,FAST和ORB的简单介绍-CSDN博客

下面让我们先了解一下重要的几个函数方法:

1.计算检测关键点并计算描述符

detectAndCompute(InputArray img, bool useProvidedKeypoints = false)

img:为cv.imread()读入的图像矩阵

useProvidedKeypoints:决定当前是探测关键点还是计算描述符,useProvidedKeypoints为true时,执行compute功能,计算描述符;为false时,执行detect功能,探测关键点

示例代码:

import cv2
img_m=cv2.imread('learn_test3.jpg',0)
img_m = cv2.resize(img_m,(500,700))
sift=cv2.SIFT_create()#定义一个SIFT特征计算对象
kp2,des2=sift.detectAndCompute(img_m,None)#,useProvidedKeypoints=False
res_r = cv2.drawKeypoints(img_m, kp2, img_m, color=(0, 255, 0), flags=0)#关键点描绘
cv2.imshow('picture2',res_r)#图片展示
cv2.waitKey(0)

探测关键点展示 :

Print(des2)
[[11.  2.  1. ...  0.  0.  0.]
 [ 0.  1.  0. ... 28.  0.  6.]
 [ 0.  0.  6. ...  0.  0.  3.]
 ...
 [ 5.  3.  7. ...  0.  0.  0.]
 [ 0.  0.  0. ...  0.  0.  0.]
 [ 2.  4.  5. ...  0.  0.  0.]]
#des2,kp2均有值

useProvidedKeypoints=True展示 :

 

Print(des2)
None
#des2,kp2均为空

2.特征匹配方法: 

 暴力匹配(Brute-Force Matcher):cv::BFMatcher

暴力匹配很简单。首先在模板特征点描述符的集合当中找到第一个特征点,然后匹配目标图片的特征点描述符集合当中的所有特征点,匹配方式使用“距离”来衡量,返回“距离”最近的那个。该方法包含两个可选参数:normType、crossCheck。
normType指定了要使用的“距离”测量方法。缺省条件下,的参数是cv2.NORM_L2。
在使用SIFT方法和SURF方法等等进行匹配时,这种“距离”测量方法效果很好(cv2.NORM_L1也一样)。在使用基于二进制字符串的描述符,像ORB,BRIEF,BRISK等等方法时,应该使用cv2.NORM_HAMMING,这种方法使用汉明距离来测量。如果ORB算法的参数设置为WAT_K==3或者4,那么应该使用cv2.NORM_HAMMING2。
crossCheck参数是boolean类型,缺省的情况下是false。如果crossCheck是true,那么匹配器返回只返回一个最佳的匹配(i,j),其中i在特征点描述符集合A当中,j在特征点描述符集合B当中,反之亦然。也就是两个集合当中的特征点都彼此配对。这种方法返回了一致的结果,并且可以很好的用来替代SIFT算法论文当中测试。

暴力匹配器有两个重要的方法,分别是BFMatcher.match()BFMatcher.knnMatch()。第一个返回最佳匹配,第二个返回前k个最佳的匹配,k值由用户指定。

cv::match方法

match匹配的返回结果是DMatch类型。

DMatch数据结构包含三个非常重要的数据分别是queryIdx,trainIdx,distance;
queryIdx:某一特征点在本帧图像的索引;
trainIdx:trainIdx是该特征点在另一张图像中相匹配的特征点的索引;
distance:代表这一对匹配的特征点描述符的欧式距离,数值越小也就说明俩个特征点越相近

matches = bf.match(des1,des2)
matches = sorted(matches,key=lambda x: x.distance)

通过对返回来的匹配数组,利用x.distance作为大小来进行排序,从而达到距离以小到大递增,则特征从大到小递减。 

示例代码

import cv2

img_f=cv2.imread('learn_test4.jpg')
img_m=cv2.imread('learn_test3.jpg')
sift=cv2.SIFT_create()

kp1,des1=sift.detectAndCompute(img_f,None,useProvidedKeypoints=False)#
kp2,des2=sift.detectAndCompute(img_m,None,useProvidedKeypoints=False)#

res_l = cv2.drawKeypoints(img_f, kp1, img_f, color=(0, 255, 0), flags=0)
res_r = cv2.drawKeypoints(img_m, kp2, img_m, color=(0, 255, 0), flags=0)

bf=cv2.BFMatcher(crossCheck=True)#用来比较a对b的路径是最短,同时检验b对a的路径是否也是最短的
matches=bf.match(des1,des2)#暴力匹配
matches=sorted(matches,key=lambda x:x.distance)#排序匹配得分,由小到大,通过两点的距离排序
img3=cv2.drawMatches(img_f,kp1,img_m,kp2,matches[:10],None,flags=2)#选取距离最小的十个点进行描绘,合并两个图片
img3=cv2.pyrDown(img3)#因为匹配之后会通过金字塔膨胀图片,所以要通过金字塔压缩一次图片
cv2.imshow('picture',img3)
cv2.waitKey(0)

 效果展示

cv::knn_Match方法

knnMatch匹配的返回结果是一个元组,说明结果不能改变;

对元组内元素进行类型查询: 所以Knnmatch与match的返回值类型一样,只不过一组返回的2个DMatch类型。

matches=bf.knnMatch(des1,des2,k=2)
good=[]
for m,n in matches:
    if m.distance<0.75*n.distance:
        good.append([m])

当 k=2 时,knnMatch 方法会返回每个查询特征点的两个最近邻匹配。每个匹配对象都包含两个最近邻的特征点,即有两个元素的列表。 例如,如果 matches 是 [(m1, n1), (m2, n2), ..., (mk, nk)],那么对于第 i 个查询特征点,它的两个最近邻匹配分别是 (mi, ni)。 以下是对每个匹配对象中元素的解释:

m.distance: 第一个最近邻的相似性度量值。距离值越小表示越相似。

n.distance: 第二个最近邻的相似性度量值。

 所以判断如果m的距离小于n的距离*0.75,则判断m点特征点较为匹配,也可以取小于0.75的值

示例代码

import cv2


img_f=cv2.imread('learn_test4.jpg')
img_m=cv2.imread('learn_test3.jpg')

sift=cv2.SIFT_create()

kp1,des1=sift.detectAndCompute(img_f,None,useProvidedKeypoints=False)#
kp2,des2=sift.detectAndCompute(img_m,None,useProvidedKeypoints=False)#

res_l = cv2.drawKeypoints(img_f, kp1, img_f, color=(0, 255, 0), flags=0)
res_r = cv2.drawKeypoints(img_m, kp2, img_m, color=(0, 255, 0), flags=0)

bf=cv2.BFMatcher()

matches=bf.knnMatch(des1,des2,k=3)
good=[]
for m,n,p in matches:
    if m.distance<0.75*n.distance:
        good.append([m])
img3=cv2.drawMatchesKnn(img_f,kp1,img_m,kp2,good,None,flags=2)
img3=cv2.pyrDown(img3)
cv2.imshow('pic1',img3)
cv2.waitKey(0)

结果演示

快速近似最近邻匹配(FLANN)

flann = cv2.FlannBasedMatcher(index_params, search_params)

index_params字典类型,在设置中用数字表示

search_params字典类型,默认dict(checks=32, eps=0,sorted=True)

名称 备注
FLANN_INDEX_LINEAR 0 线性暴力搜索
FLANN_INDEX_KDTREE 1 随机kd树,平行搜索,默认trees=4
FLANN_INDEX_KMEANS 2 层次k均值树
FLANN_INDEX_COMPOSITE 3 随机kd树和层次k均值树构建索引
FLANN_INDEX_KDTREE_SINGLE 4
FLANN_INDEX_HIERARCHICAL 5
FLANN_INDEX_LSH 6
FLANN_INDEX_SAVED 254
FLANN_INDEX_AUTOTUNED 255

FLANN的意思是快速最近邻搜索库。它包含一个对大数据集合和高维特征实现最近邻搜索的算法集合,而且这些算法是优化固偶读。面对大数据集时,效果要比暴力搜索好。
FLANN要传递两个字典作为参数,第一个参数是使用的搜索算法;第二个参数是搜索次数,次数越多,结果越精确,但是速度也越慢。

示例代码

import cv2

img_f=cv2.imread('learn_test4.jpg')
img_m=cv2.imread('learn_test3.jpg')

sift=cv2.SIFT_create()

kp1,des1=sift.detectAndCompute(img_f,None,useProvidedKeypoints=False)
kp2,des2=sift.detectAndCompute(img_m,None,useProvidedKeypoints=False)

res_l = cv2.drawKeypoints(img_f, kp1, img_f, color=(0, 255, 0), flags=0)
res_r = cv2.drawKeypoints(img_m, kp2, img_m, color=(0, 255, 0), flags=0)

algorithm_param = dict(algorithm = 2, tree = 5)#FlannBasedMatcher的参数之一,使用哪种匹配算法进行匹配
check_param = dict(check = 5)#FlannBasedMatcher的参数之一,设置匹配次数,次数越多越准确但越慢

bf = cv2.FlannBasedMatcher(algorithm_param,check_param)
matches = bf.match(des1,des2)
matches = sorted(matches,key=lambda x: x.distance)
img3 = cv2.drawMatches(img_f,kp1,img_m,kp2,matches,None,flags=2)
img3 = cv2.pyrDown(img3)
cv2.imshow('pic',img3)
cv2.waitKey(0)

结果展示

本次实验展示了特征检测和描述中的图片修复功能,官方文档请看OpenCV: OpenCV Tutorials

如有错误或遗漏,希望小伙伴批评指正!!!! 

希望这篇博客对你有帮助!!!!

实验一: 计算机视觉Opencv实验合集——实验一:图像修复-CSDN博客

实验三:Opencv实验合集——实验三:背景减除-CSDN博客

猜你喜欢

转载自blog.csdn.net/newxiaoou/article/details/134961861