OpenCV(25-2-6):cv2.feature2d模块之二:Opencv的xfeatures2d模块
实验证明,ORB远优于之前的SIFT与SURF算法,ORB算法的速度是sift的100倍,是surf的10倍。kNN算法则是从训练集中找到和新数据最接近的k条记录,然后根据他们的主要分类来决定新数据的类别。kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。由于kNN方法主要靠周围有限的邻近的样本,而不是靠判
25.2.6 Opencv的xfeatures2d模块
xfeature2d模块是contrib中对于feature2d模块的扩展,该模块用于2D特征检测,描述与匹配。其中包含的功能受专利保护或功能较新用于测试。由于feature2d模块主要用途是特征点检测,而特征点匹配也与之密切相关,因此匹配器也一并介绍。
同时拥有检测器和提取器
1、cv::xfeatures2d::SIFT
ScaleInvariant Feature Transform,尺度不变特征变换算法
高斯差分(DoG)去近似高斯拉普拉斯算子(LoG)
描述子128维,512字节
具有尺度不变性,旋转不变性。计算精度高,但内存消耗大,计算量大速度慢
2、cv::xfeatures2d::SURF
SpeededUpRobustFeatures,加速鲁棒性特征,是SIFT的加速版,特征点检测的速度有着极大的提升
盒子滤波器去近似高斯拉普拉斯算子(LoG)
描述子64维或128维,256或512字节。
具有尺度不变性,旋转不变性(允许不使用旋转不变性以提高速度)。在性能相当的情况下它比 SIFT 快 3 倍,擅长处理模糊和旋转的图像,但不善于处理视角变化和光照变化,内存消耗仍然大。
3、cv::ORB
Oriented FAST and Rotated BRIEF, 即 定向的FastDetector+旋转的BriefDescriptorExtractor
匹配时使用汉明距离匹配
具有一定的尺度不变性(由图像金字塔实现,而图像金字塔层数有限),旋转不变性(BRIEF本身不具有,ORB进行改进),ORB 比 SURF 和 SIFT 快得多,可获得实时效果
4、cv::BRISK
5、cv::KAZE
6、cv::AKAZE
特征点检测器
- cv::GFTTDetector
- cv::FastFeatureDetector
- cv::AgastFeatureDetector
- cv::SimpleBlobDetector
- cv::xfeatures2d::MSDDetector
- cv::xfeatures2d::StarDetector
- cv::xfeatures2d::HarrisLaplaceFeatureDetector
描述子提取器
1、cv::xfeatures2d::BriefDescriptorExtractor
BinaryRobustIndependentElementaryFeatures,二进制独立鲁棒基本特征
将描述子使用 PCA,LDA 等几种方法对其进行压缩,匹配用正常距离匹配
使用 LSH(Locality Sensitive Hashing,局部敏感哈希)等方法可将 SIFT 描述子转换为二进制字符串,匹配时需要用汉明距离进行匹配
字符串为16、32(默认)、64字节
不具有旋转不变性,计算和匹配特征点描述子更快,需要先得到描述子再计算为字符串,并不能解决内存问题
2、cv::MSER
3、cv::xfeatures2d::BoostDesc
4、cv::xfeatures2d::FREAK
5、cv::xfeatures2d::DAISY
6、cv::xfeatures2d::LUCID
7、cv::xfeatures2d::LATCH
8、cv::xfeatures2d::VGG
描述子匹配器
- cv::BFMatcher Brute-Force匹配器(BF),暴力匹配器, 可以获得最优结果,但花费时间长
- cv::FlannBasedMatcher FLANN匹配器,即基于快速近似最近邻算法的匹配器,对于大型数据集,它比 BFMatcher 工作得更快
5.2.7 OpenCV中xfeature2d的使用
有两张图片,对两张图片进行sift操作的步骤如下:
第一步:读入图片
第二步:进行灰度化
第三步:使用cv2. Function_Name_create() 实例化Function_Name (如SHIFT)函数
- sift = cv2.xfeatures2d.SIFT_create(),SIFT函数实例化
- surf = cv2.xfeatures2d.SURF_create(),SURF函数实例化
- orb = cv2.ORB_create(), ORB函数实例化
- brisk = cv2.BRISK_create(), BRISK函数实例化
- kaze = cv2.KAZE_create(), KAZE函数实例化
- akaze = cv2.AKAZE_create(), AKAZE函数实例化
第四步:使用sift.detectAndCompute(gray, None) 检测图像gray的关键点keypoint并计算描述子(关键点对应的128个特征向量)des
keypoint, des = sift.detectAndCompute(gray,None)
第五步:使用cv2.drawKeypoints 进行画图操作
对图像img上的关键点keypoint绘制红色圆圈
img1 = cv2.drawKeypoints(img,keypoint,img,color=(0,0,255))
对两张图片的特征向量进行匹配的步骤如下:
第六步:选择匹配方法
- 暴力匹配:bf = cv2.BFMatcher()
- matches = bf.knnMatch(des1,des2, k=2)
- FLANN匹配: matches = flann.knnMatch(des1,des2,k=2)
- matchesMask = [[0,0] for i in range(len(matches))]
第七步:调整ratio
第八步:cv2.drawMatchesKnn()绘制匹配结果
基于BFmatcher的SIFT实现
BFmatcher(Brute-Force Matching)暴力匹配,应用BFMatcher.knnMatch( )函数来进行核心的匹配,knnMatch(k-nearest neighbor classification)k近邻分类算法。
kNN算法则是从训练集中找到和新数据最接近的k条记录,然后根据他们的主要分类来决定新数据的类别。该算法涉及3个主要因素:训练集、距离或相似的衡量、k的大小。kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
kNN方法在类别决策时,只与极少量的相邻样本有关。由于kNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,kNN方法较其他方法更为适合。
经检验 BFmatcher在做匹配时会耗费大量的时间。
import numpy as np
import cv2
from matplotlib import pyplot as plt
sift = cv2.xfeatures2d.SIFT_create()
img1 = cv2.imread('C:/Users/xxx/Downloads/lena.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #灰度处理图像
kp1, des1 = sift.detectAndCompute(img1,None) #des是描述子
img2 = np.zeros(img1.shape, dtype="uint8") # 生成image大小的全黑图
img2[250:300,400:600,:]=img1[250:300,400:600,:] # 从img1中抠出一块给img2
#img2 = img1[200:300,200:300,:].copy()
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)#灰度处理图像
kp2, des2 = sift.detectAndCompute(img2,None) #des是描述子
hmerge = np.hstack((gray1, gray2)) #水平拼接
cv2.imshow("gray", hmerge) #拼接显示为gray
cv2.waitKey(0)
img3 = cv2.drawKeypoints(img1,kp1,img1,color=(255,0,255)) #画出特征点,并显示为红色圆圈
img4 = cv2.drawKeypoints(img2,kp2,img2,color=(255,0,255)) #画出特征点,并显示为红色圆圈
hmerge = np.hstack((img3, img4)) #水平拼接
cv2.imshow("point", hmerge) #拼接显示为gray
cv2.waitKey(0)
# BFMatcher解决匹配
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)
# 调整ratio
good = []
for m,n in matches:
if m.distance < 0.75*n.distance:
good.append([m])
img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,flags=2)
cv2.imshow("BFmatch", img5)
cv2.waitKey(0)
cv2.destroyAllWindows()
在cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,flags=2)下的匹配效果,比较杂乱,且会出错。

如果更换为cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2),明显优于上面的匹配,并且为预想的匹配区域,其效果为:

基于FlannBasedMatcher的SIFT实现
FLANN(Fast_Library_for_Approximate_Nearest_Neighbors)快速最近邻搜索包,它是一个对大数据集和高维特征进行最近邻搜索的算法的集合,而且这些算法都已经被优化过了。在面对大数据集时它的效果要好于 BFMatcher。
经验证,FLANN比其他的最近邻搜索软件快10倍。使用 FLANN 匹配,我们需要传入两个字典作为参数。这两个用来确定要使用的算法和其他相关参数等。
第一个是 IndexParams。
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) 。
这里使用的是KTreeIndex配置索引,指定待处理核密度树的数量(理想的数量在1-16)。
第二个字典是SearchParams。
search_params = dict(checks=100)用它来指定递归遍历的次数。值越高结果越准确,但是消耗的时间也越多。实际上,匹配效果很大程度上取决于输入。
5kd-trees和50checks总能取得合理精度,而且短时间完成。在之下的代码中,丢弃任何距离大于0.7的值,则可以避免几乎90%的错误匹配,但是好的匹配结果也会很少。
import numpy as np
import cv2
from matplotlib import pyplot as plt
sift = cv2.xfeatures2d.SIFT_create()
# FLANN 参数设计
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)
img1 = cv2.imread('C:/Users/xxx/Downloads/lena.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #灰度处理图像
kp1, des1 = sift.detectAndCompute(img1,None) #des是描述子
img2 = np.zeros(img1.shape, dtype="uint8") # 生成image大小的全黑图
img2[250:300,400:600,:]=img1[250:300,400:600,:] # 从img1中抠出一块给img2
#img2 = img1[200:300,200:300,:].copy()
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)#灰度处理图像
kp2, des2 = sift.detectAndCompute(img2,None) #des是描述子
hmerge = np.hstack((gray1, gray2)) #水平拼接
cv2.imshow("gray", hmerge) #拼接显示为gray
cv2.waitKey(0)
img3 = cv2.drawKeypoints(img1,kp1,img1,color=(255,0,255)) #画出特征点,并显示为红色圆圈
img4 = cv2.drawKeypoints(img2,kp2,img2,color=(255,0,255)) #画出特征点,并显示为红色圆圈
hmerge = np.hstack((img3, img4)) #水平拼接
cv2.imshow("point", hmerge) #拼接显示为gray
cv2.waitKey(0)
# FlannBasedMatcher解决匹配
matches = flann.knnMatch(des1,des2,k=2)
matchesMask = [[0,0] for i in range(len(matches))]
# 调整ratio
good = []
for m,n in matches:
if m.distance < 0.7*n.distance:
good.append([m])
#img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,flags=2)
img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)
cv2.imshow("FLANN", img5)
cv2.waitKey(0)
cv2.destroyAllWindows()
在cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,flags=2)下的匹配效果,比较杂乱,且会出错。

如果更换为cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2),明显优于上面的匹配,并且为预想的匹配区域,其效果为:

修改if m.distance < 0.7*n.distance:为 if m.distance < 1*n.distance:,显示效果为:

可见,虽然值越大,匹配的线条越密集,但错误匹配点也会增多,在lowe论文中,Lowe推荐ratio的阈值为0.8,但作者对大量任意存在尺度、旋转和亮度变化的两幅图片进行匹配,结果表明ratio取值在0. 4~0. 6 之间最佳,小于0. 4的很少有匹配点,大于0. 6的则存在大量错误匹配点,所以建议ratio的取值原则如下:
- ratio=0. 4:对于准确度要求高的匹配;
- ratio=0. 6:对于匹配点数目要求比较多的匹配;
- ratio=0. 5:一般情况下。
修改if m.distance < 0.7*n.distance:为 if m.distance < 0.1*n.distance:,显示效果为:

基于FlannBasedMatcher的SURF实现
SURF全称为“加速稳健特征”(Speeded Up Robust Feature),不仅是尺度不变特征,而且是具有较高计算效率的特征。可被认为SURF是尺度不变特征变换算法(SIFT算法)的加速版。SURF最大的特征在于采用了haar特征以及积分图像的概念,SIFT采用的是DoG图像,而SURF采用的是Hessian矩阵(SURF算法核心)行列式近似值图像。SURF借鉴了SIFT算法中简化近似的思想,实验证明,SURF算法较SIFT算法在运算速度上要快3倍,综合性优于SIFT算法。
import numpy as np
import cv2
from matplotlib import pyplot as plt
surf = cv2.xfeatures2d.SURF_create()
# FLANN 参数设计
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)
img1 = cv2.imread('C:/Users/xxx/Downloads/lena.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #灰度处理图像
kp1, des1 = surf.detectAndCompute(img1,None) #des是描述子
img2 = np.zeros(img1.shape, dtype="uint8") # 生成image大小的全黑图
img2[250:300,400:600,:]=img1[250:300,400:600,:] # 从img1中抠出一块给img2
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)#灰度处理图像
kp2, des2 = surf.detectAndCompute(img2,None) #des是描述子
hmerge = np.hstack((gray1, gray2)) #水平拼接
cv2.imshow("gray", hmerge) #拼接显示为gray
cv2.waitKey(0)
img3 = cv2.drawKeypoints(img1,kp1,img1,color=(255,0,255)) #画出特征点,并显示为红色圆圈
img4 = cv2.drawKeypoints(img2,kp2,img2,color=(255,0,255)) #画出特征点,并显示为红色圆圈
hmerge = np.hstack((img3, img4)) #水平拼接
cv2.imshow("point", hmerge) #拼接显示为gray
cv2.waitKey(0)
# FlannBasedMatcher解决匹配
matches = flann.knnMatch(des1,des2,k=2)
matchesMask = [[0,0] for i in range(len(matches))]
# 调整ratio
good = []
for m,n in matches:
#if m.distance < 0.7*n.distance:
#if m.distance < 1.0*n.distance:
if m.distance < 0.1*n.distance:
good.append([m])
#img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,flags=2)
img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)
cv2.imshow("SURF", img5)
cv2.waitKey(0)
cv2.destroyAllWindows()
基于BFMatcher的ORB实现
ORB(Oriented Fast and Rotated BRIEF),结合Fast与Brief算法,并给Fast特征点增加了方向性,使得特征点具有旋转不变性,并提出了构造金字塔方法,解决尺度不变性,但文章中没有具体详述。特征提取是由FAST(Features from Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust Independent Elementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。ORB主要解决BRIEF描述子不具备旋转不变性的问题。实验证明,ORB远优于之前的SIFT与SURF算法,ORB算法的速度是sift的100倍,是surf的10倍。
import numpy as np
import cv2
from matplotlib import pyplot as plt
orb = cv2.ORB_create()
img1 = cv2.imread('C:/Users/xxx/Downloads/lena.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #灰度处理图像
kp1, des1 = orb.detectAndCompute(img1,None) #des是描述子
img2 = np.zeros(img1.shape, dtype="uint8") # 生成image大小的全黑图
img2[250:300,400:600,:]=img1[250:300,400:600,:] # 从img1中抠出一块给img2
#img2 = img1[200:300,200:300,:].copy()
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)#灰度处理图像
kp2, des2 = orb.detectAndCompute(img2,None) #des是描述子
hmerge = np.hstack((gray1, gray2)) #水平拼接
cv2.imshow("gray", hmerge) #拼接显示为gray
cv2.waitKey(0)
img3 = cv2.drawKeypoints(img1,kp1,img1,color=(255,0,255)) #画出特征点,并显示为红色圆圈
img4 = cv2.drawKeypoints(img2,kp2,img2,color=(255,0,255)) #画出特征点,并显示为红色圆圈
hmerge = np.hstack((img3, img4)) #水平拼接
cv2.imshow("point", hmerge) #拼接显示为gray
cv2.waitKey(0)
# BFMatcher解决匹配
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)
# 调整ratio
good = []
for m,n in matches:
#if m.distance < 0.75*n.distance:
if m.distance < 0.5*n.distance:
good.append([m])
#img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,flags=2)
img5 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)
cv2.imshow("BFmatch", img5)
cv2.waitKey(0)
cv2.destroyAllWindows()

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)