文章目录
环境:python3.7 + opencv3.4.2
后文运用SIFT和SURF,需安装opencv-contrib-python
pip uninstall opencv-python
pip install opencv-contrib-python
一、检测特征点
1.sift检测特征点
import cv2
from matplotlib import pyplot as plt
# 绘图的一些基本库
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
img = cv2.imread('D:/myworkspace/dataset/My_test/img/cameraback.png',0)# 输入灰度图
# 构造生成器
sift = cv2.xfeatures2d.SIFT_create()
# 检测图片
kp,des = sift.detectAndCompute(img,None) # 关键点(Keypoint)和描述子(Descriptor)
# 绘出关键点
img2=cv2.drawKeypoints(img,kp,None,(255,0,0),4)
# 显示
plt.figure(figsize=(10,10)) # 画布放大10倍
plt.title('sift检测特征点',fontsize=20) # 标题
plt.axis('off') # 隐藏坐标轴
plt.imshow(img2) # 打印图像
plt.show() # 显示画布
# 释放资源
cv2.waitKey(0)
cv2.destroyAllWindows()
- SIFT特征(ScaleInvariant Feature Transform,尺度不变特征转换):图像尺度不变性、旋转不变性
- cv2.drawKeypoints (原始图片,关键点,输出 ,颜色,绘图功能的标识)
flag标识为4:cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,他会以一种丰富的方式画出来,效果会好点。
2.surf检测特征点
import cv2
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
img = cv2.imread('D:/myworkspace/dataset/My_test/img/cameraback.png',0)
surf = cv2.xfeatures2d.SURF_create()
kp,des = surf.detectAndCompute(img,None)
img2=cv2.drawKeypoints(img,kp,None,(255,0,0),4)
plt.figure(figsize=(10,10))
plt.title('surf检测特征点',fontsize=20)
plt.axis('off')
plt.imshow(img2)
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
- SURF(Speeded Up Robust Features,加速鲁棒性特征)
对SIFT特征的进一步优化,Sift采用的是DOG图像,而surf采用的是Hessian矩阵行列式近似值图像,也写作DOH算子。
效果:
3.brisk检测特征点
import cv2
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
img = cv2.imread('D:/myworkspace/dataset/My_test/img/cameraback.png',0)
brisk = cv2.BRISK_create()
kp, des = brisk.detectAndCompute(img,None)
img2=cv2.drawKeypoints(img,kp,None,(255,0,0),4)
plt.figure(figsize=(10,10))
plt.title('brisk检测特征点',fontsize=20)
plt.axis('off')
plt.imshow(img2)
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
- BRISK(Binary Robust Invariant Scalable Keypoints,二进制的鲁棒特征描述算子)
具有较好的旋转不变性、尺度不变性,较好的鲁棒性等。
在图像配准应用中,速度比较:SIFT<SURF<BRISK<FREAK<ORB
,
在对有较大模糊的图像配准时,BRISK算法在其中表现最为出色。
4.orb检测特征点
import cv2
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
img = cv2.imread('D:/myworkspace/dataset/My_test/img/cameraback.png',0)
orb = cv2.ORB_create()# Oriented FAST and Rotated BRIEF
kp, des = orb.detectAndCompute(img,None)
img2=cv2.drawKeypoints(img,kp,None,(255,0,0),4)
plt.figure(figsize=(10,10))
plt.title('orb检测特征点',fontsize=20)
plt.axis('off')
plt.imshow(img2)
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
- ORB(Oriented FAST and Rotated BRIEF):
将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。ORB算法的速度大约是SIFT的100倍,是SURF的10倍
。
效果:
5.akaze检测特征点
cv2.AKAZE_create()
import cv2
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
img = cv2.imread('D:/myworkspace/dataset/My_test/img/cameraback.png',0)
akaze = cv2.AKAZE_create()
kp, des = akaze.detectAndCompute(img,None)
img2=cv2.drawKeypoints(img,kp,None,(255,0,0),4)
plt.figure(figsize=(10,10))
plt.title('akaze检测特征点',fontsize=20)
plt.axis('off')
plt.imshow(img2)
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
- AKAZE ( Accelerated-KAZE,非线性尺度空间的特征点)也叫作 局部特征点匹配:AKAZE是KAZE的加速版。
通过AOS构造尺度空间、Hessian矩阵特征点检测、方向指定一阶微分图像、生成描述子,适合非线性尺度空间。
与SIFT、SURF算法相比,AKAZE算法更快同时与ORB、BRISK算法相比,可重复性与鲁棒性提升很大
。
6.总结
运行速度和鲁棒性比较:SIFT<SURF<BRISK<FREAK<ORB<AKAZE
二、特征匹配
匹配原理参考:特征提取与匹配—SURF;SIFT;ORB;FAST;Harris角点
源码参考:python opencv3 基于ORB的特征检测和 BF暴力匹配 knn匹配 flann匹配
1.暴力匹配法
(又称作:交叉匹配)
交叉过滤的思想很简单,再进行一次匹配,反过来使用被匹配到的点进行匹配,如果匹配到的仍然是第一次匹配的点的话,就认为这是一个正确的匹配。
eg:假如第一次特征点A使用暴力匹配的方法,匹配到的特征点是特征点B;反过来,使用特征点B进行匹配,如果匹配到的仍然是特征点A,则就认为这是一个正确的匹配,否则就是一个错误的匹配。
import numpy as np
import cv2
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
img1 = cv2.imread(
"D:/myworkspace/dataset/My_test/img/a_img/airplane_30.jpg", 0) # 导入灰度图像
img2 = cv2.imread(
"D:/myworkspace/dataset/My_test/img/a_img/airplane_66.jpg", 0)
detector = cv2.ORB_create() # Oriented FAST and Rotated BRIEF
# detector = cv2.AKAZE_create()
kp1 = detector.detect(img1, None)
kp2 = detector.detect(img2, None)
kp1, des1 = detector.compute(img1, kp1) # keypoint 是关键点的列表,desc 检测到的特征的局部图的列表
kp2, des2 = detector.compute(img2, kp2)
# 获得一个暴力匹配器的对象
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 利用匹配器 匹配两个描述符的相近程度
"""暴力法"""
matches = bf.match(des1, des2)
# 按照相近程度 进行排序
matches = sorted(matches, key=lambda x: x.distance)
# 画出匹配项
img31 = cv2.drawMatches(img1, kp1, img2, kp2, matches[: 50], img2, flags=2)
plt.figure(figsize=(10,10))
plt.title('暴力法',fontsize=12)
plt.imshow(img31)
plt.axis('off')
plt.show()
"""knn法"""
matches = bf.knnMatch(des1, des2, k=1) # knn 匹配可以返回k个最佳的匹配项、bf返回所有的匹配项
img32 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches[: 50], img2, flags=2)
plt.figure(figsize=(10,10))
plt.title('knn法',fontsize=12,color='r')
plt.imshow(img32)
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.drawMatches()
使用的点对集keypoint是一维的,(N,);
matchesMask是计算转换H矩阵时生成的掩模,inliers的点,最后只画出符合条件的inliers点对。cv2.drawMatchesKnn()
使用的点对集keypoint是一维的,(N,1);
画出keypoin中前几个点对连线。
优化:
- 对 matches 匹配点进行排序,提取匹配效果最优的50个点。过滤掉相似性低的特征点。
2.KNN匹配法
K近邻匹配,在匹配的时候选择K个和特征点最相似的点,如果这K个点之间的区别足够大,则选择最相似的那个点作为匹配点,通常选择K = 2,也就是最近邻匹配。对每个匹配返回两个最近邻的匹配,如果第一匹配和第二匹配距离比率足够大(向量距离足够远),则认为这是一个正确的匹配,比率的阈值通常在2左右。
knn匹配过程中很可能发生错误的匹配,错误的匹配主要有两种:
匹配的特征点事错误的,图像上的特征点无法匹配。
KNNMatch()
:暴力法的基础上添加比率测试。
-
交叉过滤 (暴力法)
如果第一幅图像的一个特征点和第二幅图像的一个特征点相匹配,则进行一个相反的检查,即将第二幅图像上的特征点与第一幅图像上相应特征点进行匹配,如果匹配成功,则认为这对匹配是正确的。OpenCV中的BFMatcher已经包含了这种过滤 BFMatcher matcher(NORM_L2,true),在构造BFMatcher是将第二个参数设置为true。
-
比率测试 (knn)
KNNMatch,可设置K = 2 ,即对每个匹配返回两个最近邻描述符,仅当第一个匹配与第二个匹配之间的距离足够小时,才认为这是一个匹配。
3.FLANN匹配法
FLANN是快速最近邻搜索算法寻找
(用快速的第三方库近似最近邻搜索算法)。它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。对于大型数据集,它的运行速度比BFMatcher快。
import cv2
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
"""
FLANN是类似最近邻的快速匹配库
它会根据数据本身选择最合适的算法来处理数据
比其他搜索算法快10倍
"""
# 按照灰度图片读入
img1 = cv2.imread("D:/myworkspace/dataset/My_test/img/a_img/airplane_30.jpg",cv2.IMREAD_GRAYSCALE)#导入灰度图像
img2 = cv2.imread("D:/myworkspace/dataset/My_test/img/a_img/airplane_66.jpg",cv2.IMREAD_GRAYSCALE)
# 创建sift检测器
sift = cv2.xfeatures2d.SIFT_create()
# 查找监测点和匹配符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
"""
keypoint是检测到的特征点的列表
descriptor是检测到特征的局部图像的列表
"""
# 获取flann匹配器
FLANN_INDEX_KDTREE = 0
# 参数1:indexParams
# 对于SIFT和SURF,可以传入参数index_params=dict(algorithm=FLANN_INDEX_KDTREE, trees=5)。
# 对于ORB,可以传入参数index_params=dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12)。
indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
# 参数2:searchParams 指定递归遍历的次数,值越高结果越准确,但是消耗的时间也越多。
searchParams = dict(checks=50)
# 使用FlannBasedMatcher 寻找最近邻近似匹配
flann = cv2.FlannBasedMatcher(indexParams, searchParams)
# 使用knnMatch匹配处理,并返回匹配matches
matches = flann.knnMatch(des1, des2, k=2)
# 通过掩码方式计算有用的点
matchesMask = [[0, 0] for i in range(len(matches))]
# 通过描述符的距离进行选择需要的点
for i, (m, n) in enumerate(matches):
if m.distance < 0.7*n.distance: # 通过0.7系数来决定匹配的有效关键点数量
matchesMask[i] = [1, 0]
drawPrams = dict(matchColor=(0, 255, 0),
singlePointColor=(255, 0, 0),
matchesMask=matchesMask,
flags=0)
# 匹配结果图片
img33 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None, **drawPrams)
plt.figure(figsize=(10,10))
plt.title('FLANN法',fontsize=12,color='r')
plt.imshow(img33)
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
4.总结
特征匹配:FLANN效果更好一些。
至于如何选择特征点检测算法 参考第一部分。
相关章节快速门: