Python OpenCV学习之特征点检测与匹配详解
背景
提取图像的特征点是图像领域中的关键任务,不管在传统还是在深度学习的领域中,特征代表着图像的信息,对于分类、检测任务都是至关重要的;
特征点应用的一些场景:
图像搜索:以图搜图(电商、教育领域)
图像拼接:全景拍摄(关联图像拼接)
拼图游戏:游戏领域
一、harris角点
哈里斯角点检测主要有以下三种情况:
- 光滑区域:无论向哪个方向移动,衡量系数不变;
- 边缘区域:垂直边缘移动时,衡量系数变化强烈;
- 角点区域:不管往哪个方向移动,衡量系数变化强烈;
函数原型:
cornerharris(img,blocksize,ksize,k)
blocksize
:检测窗口大小;
k
:权重系数,一般取0.02~0.04之间;
代码案例:
img = cv2.imread('chess.png') gray = cv2.cvtcolor(img, cv2.color_bgr2gray) dst = cv2.cornerharris(gray, 2, 3, 0.04) img[dst > 0.01*dst.max()] = (0, 0, 255) cv2.imshow('harris', img) cv2.waitkey(0)
二、shi-tomasi角点检测
说明:是harris角点检测的改进,在harris中需要知道k这个经验值,而在shi-tomasi不需要;
函数原型:
goodfeaturestotrack(img,…)
maxcorners
:角点的最大数量,值为0表示所有;
qualitylevel
:角点的质量,一般在0.01~0.1之间(低于的过滤掉);
mindistance
:角点之间最小欧式距离,忽略小于此距离的点;
mask
:感兴趣区域;
useharrisdetector
:是否使用harris算法(默认为false)
代码案例:
img = cv2.imread('chess.png') gray = cv2.cvtcolor(img, cv2.color_bgr2gray) dst = cv2.goodfeaturestotrack(gray, 1000, 0.01, 10) dst = np.int0(dst) # 实际上也是np.int64 for i in dst: x, y = i.ravel() # 数组降维成一维数组(inplace的方式) cv2.circle(img, (x, y), 3, (0, 0, 255), -1) cv2.imshow('harris', img) cv2.waitkey(0)
本质上和harris角点检测相同,效果会好一些,角点数量会多一些;
三、sift关键点
中文简译:与缩放无关的特征转换;
说明:harris角点检测具有旋转不变性,也就是旋转图像并不会影响检测效果;但其并不具备缩放不变性,缩放大小会影响角点检测的效果;sift具备缩放不变性的性质;
实现步骤:
创建sift对象 —— 进行检测(sift.detect) —— 绘制关键点(drawkeypoints)
代码案例:
img = cv2.imread('chess.png') gray = cv2.cvtcolor(img, cv2.color_bgr2gray) sift = cv2.xfeatures2d.sift_create() kp = sift.detect(gray, none) # 第二个参数为mask区域 cv2.drawkeypoints(gray, kp, img) cv2.imshow('sift', img) cv2.waitkey(0)
四、sift描述子
首先需要说明,关键点和描述子是两个概念;
关键点:位置、大小和方向;
关键点描述子:记录了关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换,光照变换等影响;描述子的作用就是用于特征匹配;
同时计算关键点和描述子的函数(主要使用):
detectandcompute(img,…)
代码案例:
img = cv2.imread('chess.png') gray = cv2.cvtcolor(img, cv2.color_bgr2gray) sift = cv2.xfeatures2d.sift_create() kp, dst = sift.detectandcompute(gray, none) # 第二个参数为mask区域
得到的dst即为描述子的信息;
五、surf
中译:加速的鲁棒性特征检测;
说明:sift最大的缺点是速度慢,因此才会有surf(速度快);
实现步骤与sift一致,代码如下:
surf = cv2.xfeatures2d.surf_create() kp, dst = surf.detectandcompute(gray, none) # 第二个参数为mask区域 cv2.drawkeypoints(gray, kp, img)
由于安装的opencv-contrib版本过高(有版权问题),已经不支持该功能了,在此就不作展示了;
六、orb
说明:最大的优势就是做到实时检测,缺点就是缺失了很多信息(准确性下降);
主要是两个技术的结合:fast(特征点实时检测)+ briee(快速描述子建立,降低特征匹配时间)
使用步骤与之前的sift一致,代码如下:
img = cv2.imread('chess.png') gray = cv2.cvtcolor(img, cv2.color_bgr2gray) orb = cv2.orb_create() kp, dst = orb.detectandcompute(gray, none) # 第二个参数为mask区域 cv2.drawkeypoints(gray, kp, img) cv2.imshow('orb', img) cv2.waitkey(0)
可以看出,相比于sift以及surf关键点变少了,但是其速度有了很大提升;
七、暴力特征匹配(bf)
匹配原理:类似于穷举匹配机制,使用第一组中每个特征的描述子与第二组中的进行匹配,计算相似度,返回最接近的匹配项;
实现步骤:
创建匹配器:bfmatcher(normtype,crosscheck)
进行特征匹配:bf.match(des1,des2)
绘制匹配点:cv2.drawmatches(img1,kp1,img2,kp2)
代码案例:
img1 = cv2.imread('opencv_search.png') img2 = cv2.imread('opencv_orig.png') g1 = cv2.cvtcolor(img1, cv2.color_bgr2gray) g2 = cv2.cvtcolor(img2, cv2.color_bgr2gray) sift = cv2.sift_create() kp1, dst1 = sift.detectandcompute(g1, none) # 第二个参数为mask区域 kp2, dst2 = sift.detectandcompute(g2, none) # 第二个参数为mask区域 bf = cv2.bfmatcher_create(cv2.norm_l1) match = bf.match(dst1, dst2) img3 = cv2.drawmatches(img1, kp1, img2, kp2, match, none) cv2.imshow('result', img3) cv2.waitkey(0)
从上图可看出,匹配的效果还是不错的,只有一个特征点匹配错误;
八、flann特征匹配
优点:在进行批量特征匹配时,flann速度更快;
缺点:由于使用的时邻近近似值,所有精度较差;
实现步骤与暴力匹配法一致,代码如下:
img1 = cv2.imread('opencv_search.png') img2 = cv2.imread('opencv_orig.png') g1 = cv2.cvtcolor(img1, cv2.color_bgr2gray) g2 = cv2.cvtcolor(img2, cv2.color_bgr2gray) sift = cv2.sift_create() kp1, dst1 = sift.detectandcompute(g1, none) # 第二个参数为mask区域 kp2, dst2 = sift.detectandcompute(g2, none) # 第二个参数为mask区域 index_params = dict(algorithm = 1, trees = 5) search_params = dict(checks=50) flann = cv2.flannbasedmatcher(index_params, search_params) matchs = flann.knnmatch(dst1, dst2, k=2) good = [] for i, (m, n) in enumerate(matchs): if m.distance < 0.7 * n.distance: good.append(m) img3 = cv2.drawmatchesknn(img1, kp1, img2, kp2, [good], none) cv2.imshow('result', img3) cv2.waitkey(0)
上图可以看出,匹配的特征点数量相比暴力匹配明显变少了,但速度会快很多;
九、图像查找
实现原理:特征匹配 + 单应性矩阵;
单应性矩阵原理介绍:
上图中表示从两个不同角度对原图的拍摄,其中h为单应性矩阵,可通过该矩阵将图像进行转换;
下面使用两个函数实现图像查找的功能:
findhomography():获得单应性矩阵;
perspectivvetransform():仿射变换函数;
代码实现如下:
img1 = cv2.imread('opencv_search.png') img2 = cv2.imread('opencv_orig.png') g1 = cv2.cvtcolor(img1, cv2.color_bgr2gray) g2 = cv2.cvtcolor(img2, cv2.color_bgr2gray) sift = cv2.sift_create() kp1, dst1 = sift.detectandcompute(g1, none) # 第二个参数为mask区域 kp2, dst2 = sift.detectandcompute(g2, none) # 第二个参数为mask区域 index_params = dict(algorithm = 1, trees = 5) search_params = dict(checks=50) flann = cv2.flannbasedmatcher(index_params, search_params) matchs = flann.knnmatch(dst1, dst2, k=2) good = [] for i, (m, n) in enumerate(matchs): if m.distance < 0.7 * n.distance: good.append(m) if len(good) >= 4: # 获得源和目标点的数组 srcpts = np.float32([kp1[m.queryidx].pt for m in good]).reshape(-1, 1, 2) dstpts = np.float32([kp2[m.trainidx].pt for m in good]).reshape(-1, 1, 2) # 获得单应性矩阵h h, _ = cv2.findhomography(srcpts, dstpts, cv2.ransac, 5.0) h, w = img1.shape[:2] pts = np.float32([[0,0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2) # 进行放射变换 dst = cv2.perspectivetransform(pts, h) # 绘制查找到的区域 cv2.polylines(img2, [np.int32(dst)], true, (0,0,255)) else: print('good must more then 4.') exit() img3 = cv2.drawmatchesknn(img1, kp1, img2, kp2, [good], none) cv2.imshow('result', img3) cv2.waitkey(0)
总结
本篇主要介绍了特征点检测和匹配,其中重要的部分时sift算法以及flann算法;通过所学的知识,可以简单实现一个图像查找的功能,也就是找子图的功能。甚至可以目标识别的效果;当然这里需要的是完全一致的,不同于深度学习中的目标识别任务
以上就是python opencv学习之特征点检测与匹配详解的详细内容,更多关于python opencv特征点检测与匹配的资料请关注其它相关文章!
推荐阅读
-
详解opencv Python特征检测及K-最近邻匹配
-
python学习之利用opencv实现SIFT特征提取与匹配
-
Python OpenCV特征检测之特征匹配方式详解
-
Python OpenCV学习之特征点检测与匹配详解
-
OpenCV中feature2D学习——SIFT和SURF算子实现特征点提取与匹配
-
opencv_python Stitcher拼接图像实例(SIFT/SURF检测特征点,BF/FLANN匹配特征点)
-
OpenCV中feature2D学习SIFT和SURF算子实现特征点提取与匹配
-
Python OpenCV特征检测之特征匹配方式详解
-
Python OpenCV学习之特征点检测与匹配详解