Bootstrap

OpenCv特征匹配

OpenCv提供了两种描述符匹配方法:Brute-Force匹配与FLANN匹配

1.Brute-Force匹配

1.1创建BFMatcher对象

1.2使用两个方法:match()或knnMatch()进行描述符匹配 

1.3基于ORB或SIFT的BF匹配

2.FLANN匹配

2.1第一个字典是IndexParams

2.2第二个字典是SearchParams:

2.3FLANN匹配器示例


1.Brute-Force匹配

Brute-Force匹配又称蛮力匹配, 将一组特征点中的每一个特征点描述符与另一组的最接近的特征点描述符匹配。

下面介绍OpenCv中的使用流程:

1.1创建BFMatcher对象

retval = cv.BFMatcher_create([, normType[, crossCheck]]) ,参数详解如下

1.normType:NORM_L1,NORM_L2,NORM_HAMMING,NORM_HAMMING2四种可选。

SIFT与SUFT描述符应使用NORM_L1、NORM_L2。ORB、BRISK和BRIEF描述符应该使用NORM_HAMMING。使用ORB描述符但当WTA_K等于3或4时应该选用NORM_HAMMING2。

2.crossCheck:默认为FALSE。如果设置为TRUE,只有当两组中特征点互相匹配时才算匹配成功。也就是说A组中x点描述符的最佳匹配点是B组的y点,那么B组的y点的描述符最佳匹配点也要是A组的x点才算匹配成功。

1.2使用两个方法:match()或knnMatch()进行描述符匹配 

二者的区别是match()返回最佳匹配,knnMathch()返回最佳的k个匹配。

matches = cv.DescriptorMatcher.match( queryDescriptors, trainDescriptors[, mask] )

# 创建BF匹配器对象
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True)

# 特征点描述符匹配
matches = bf.match(des1,des2)

# 距离排序
matches = sorted(matches, key = lambda x:x.distance)

# 画出前30匹配
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:30], None, flags=2)

matches = cv.DescriptorMatcher.knnMatch( queryDescriptors, trainDescriptors, k[, mask[, compactResult]] )

**注意,两种方法对应的画点方法也不一样。 

# 创建BF匹配器对象
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True)

# 特征点描述符匹配
matches = bf.knnMatch(des1,des2,k=1)

# 画出前30匹配
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches[:30], None, flags=2)

**关于Matcher对象: 

可能会有人好奇返回的变量matches是什么?其实是一种DMatch数据结构的列表。

DMatch结构含有: 

DMatch.distance:描述符之间的距离,越低越好。

DMatch.queryIdx:主动匹配的描述符组中描述符的索引。

DMatch.trainIdx:被匹配的描述符组中描述符的索引。

DMatch.imgIdx:目标图像的索引。

更详细见:

opencv中match与KnnMatch返回值解释 

1.3基于ORB或SIFT的BF匹配

# 使用ORB描述符进行Brute-Force匹配:

import cv2

img1 = cv2.imread('gyy.jpg')

# 设一个ROI为被匹配的图像
ROI = img1[100:350,150:300]
img2=cv2.resize(ROI,None,fx=2,fy=2,interpolation=cv2.INTER_CUBIC)

# 初始化ORB特征点检测器
orb = cv2.ORB_create()

# 检测特征点与描述符
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

# 创建蛮力(BF)匹配器
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True)

# 匹配描述符
matches = bf.match(des1,des2)

# 整理排序匹配后的描述符
matches = sorted(matches, key = lambda x:x.distance)

# 画出前10个匹配的描述符
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2)

cv2.imshow("show",img3)
cv2.waitKey()
cv2.destroyAllWindows()

# 带有SIFT描述符和比例测试的BF匹配:

import cv2

img1 = cv2.imread('gyy.jpg')

# 设一个ROI为被匹配的图像
ROI = img1[250:350,150:300]             # trainImage
img2=cv2.resize(ROI,None,fx=2,fy=2,interpolation=cv2.INTER_CUBIC)

# 初始化SIFT特征点检测器
sift = cv2.xfeatures2d.SIFT_create()

# 检测特征点与描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

# 创建蛮力(BF)匹配器
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)

# 比值测试,首先获取与 A 距离最近的点 B(最近)和 C(次近),只有当 B/C
# 小于阈值时( 0.75)才被认为是匹配,因为假设匹配是一一对应的,真正的匹配的理想距离为 0
good = []
for m,n in matches:
    if m.distance < 0.75*n.distance:
        good.append([m])

# cv.drawMatchesKnn()把列表作为匹配项。
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)

cv2.imshow("show",img3)
cv2.waitKey()
cv2.destroyAllWindows()

2.FLANN匹配

FLANN是近似最近邻的快速库。它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。对于大型数据集,它的运行速度比BFMatcher快。

基于FLANN的匹配器,我们需要传递两个字典,这些字典指定要使用的算法,其相关参数等。

2.1第一个字典是IndexParams

对于SIFT、SUFT算法应该是:

FLANN_INDEX_KDTREE = 1 
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)

对于ORB算法应该是:

FLANN_INDEX_LSH = 6
index_params= dict(algorithm = FLANN_INDEX_LSH,
                   table_number = 6, # 12
                   key_size = 12,     # 20
                   multi_probe_level = 1) #2

2.2第二个字典是SearchParams:

它指定索引中的树应递归遍历的次数。较高的值可提供更好的精度,但也需要更多时间。例如:

search_params = dict(checks = 100)

2.3FLANN匹配器示例

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

img1 = cv.imread('gyy.jpg',cv.IMREAD_GRAYSCALE)    # 索引图像
img2 = cv.imread('gyy.jpg',cv.IMREAD_GRAYSCALE)    # 训练图像

# 初始化SIFT描述符
sift = cv.xfeatures2d.SIFT_create()

# 基于SIFT找到关键点和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

# FLANN的参数
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # 或传递一个空字典
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

# 只需要绘制好匹配项,因此创建一个掩码
matchesMask = [[0,0] for i in range(len(matches))]

# 根据Lowe的论文进行比例测试
for i,(m,n) in enumerate(matches):
    if m.distance < 0.7*n.distance:
        matchesMask[i]=[1,0]
draw_params = dict(matchColor = (0,255,0),
                   singlePointColor = (255,0,0),
                   matchesMask = matchesMask,
                   flags = cv.DrawMatchesFlags_DEFAULT)
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)

plt.imshow(img3,),plt.show()
;