opencv关键点检测

特征匹配主要是基于两种相似度较高的图片,通过opencv里面提供的特征匹配方法来进行特征点之间的匹配和映射
特征点由关键点和描述子两部分组成。例如:在一张图像中计算SIFT特征点时,是指提取SIFT关键点,并计算SIFT描述子两件事。
关键点是指特征点在图像里的位置,有些特征点还具有方向、大小等信息。描述子是指一个向量,描述该关键点周围像素的信息
按照外观相似特征应该有相似的描述子的原则设计

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

1.计算检测关键点并计算描述符
detectAndCompute(InputArray img,bool useProvidedKeypoints=false)
useProvidedKeypoints:决定当前是探测关键点还是计算描述符,为true时执行compute功能;为false时,执行detect功能,探测关键点

点击查看代码
import cv2
img_m=cv2.imread("data1/1.png",0)
sift=cv2.SIFT_create()
kp2,des2=sift.detectAndCompute(img_m,None,useProvidedKeypoints=False) #useProvidedKeypoints决定当前是探测关键点还是计算描述符,
# useProvidedKeypoints为true时,执行compute功能,计算描述符;为false时,执行detect功能,探测关键点
res_r=cv2.drawKeypoints(img_m,kp2,img_m,color=(0,255,0),flags=0)
cv2.imshow('picture2',res_r)
cv2.waitKey(0)
特征匹配方法 暴力匹配(Brute-Force Matcher):cv::BFMatcher 暴力匹配很简单。首先在模版特征点描述符的集合当中找到第一个特征点,然后匹配目标图片的特征点描述符集合当中的所有特征点, 匹配方式使用"距离"来衡量,返回"距离"最近的那个。 该方法包含两个可选参数:normType、crossCheck. normType指定了要使用的"距离"测量方法。缺省条件下,的参数是cv2.NORM_L2. 在使用SIFT方法和SURF方法等等进行匹配时,这种"距离"测量方法效果很好(cv2.NORMAL_L1也一样)。 在使用基于二进制字符串的描述符,像ORB,BRIEF,BRISK等等方法时,应该使用cv2.NORM_HAMMING,这种方法使用汉明距离来测量。 如果ORB算法的参数设置为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('data1/1.png')
img_m=cv2.imread('data1/2.png')
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.75n.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点特征点较为匹配

点击查看代码
import cv2
img_f=cv2.imread('data1/1.png')
img_m=cv2.imread('data1/2.png')
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_DKTREE 1 随机kd树,平行搜索,默认trees=4
FLANN_INDEX_KMEANS 2 层次k均值树
FLANN_INDEX_COMPOSITE 3 随机kd数

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

点击查看代码
import cv2
img_f=cv2.imread('data1/1.png')
img_m=cv2.imread('data1/2.png')
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_prarm=dict(algorithm=2,tree=5) #FlannBaseMatcher的参数之一
check_param=dict(check=5) #FlannBasedMatcher的参数之一,设置匹配次数,次数越多越准确
bf=cv2.FlannBasedMatcher(algorithm_prarm,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)

posted @ 2024-08-15 18:52  阳光天气  阅读(5)  评论(0编辑  收藏  举报