python+OpenCV笔记(三十五):特征匹配——基于FLANN的匹配、基于FLANN进行单应性匹配
目录
一、基于FLANN的匹配
FLANN匹配流程:
代码编写
二、基于FLANN进行单应性匹配
什么是单应性?
FLANN进行单应性匹配流程
代码编写
FLANN库全称是Fast Library for Approximate Nearest Neighbors,它是目前最完整的(近似)最近邻开源库。不但实现了一系列查找算法,还包含了一种自动选取最快算法的机制,FLANN使用C++写成,它能够很容易地通过C,MTALAB和Python等绑定提供的库,用在很多环境中。
一、基于FLANN的匹配
FLANN匹配流程:
- 导入NumPy, OpenCV, Matplotlib,从文件加载图像(imread)
- 使用cv2.SIFT类来检测必要的关键点,并提取特征:
sift = cv2.SIFT_create() kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None)
- 之前,我们将描述符送入了BFMatcher,用于蛮力匹配。这次,我们将使用cv2.FlannBasedMatcher
index_params = dict(algorithm=1, tree=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1, des2, k=2)
FLANN匹配器接收两个参数:indexindexParams对象和searchParams对象,这些参数以Python中字典(和C++中结构体)的形式传递。
我们使用了5棵树的核密度树索引算法,FLANN可以并行处理此算法。
我们对每棵树执行50次检查或者遍历,检查次数越多,可以提供的精度也越高,但是,计算成本也就更高。 - 之后,我们应用乘数为0.7的劳氏比率检验。同时,组建一个名为mask_matches 的列表,其中每个元素都是长度为 k(与传给KnnMatch的 k 是一样的) 的子列表,如果匹配成功,则将子列表对应的元素设为1,否则设置为0.
例如,如果mask_matches=[[0,0],[1,0]],这意味着有两个匹配的关键点:对于第一个关键点,最优和次优匹配都是糟糕的,对于第二个关键点,最佳匹配是好的,次优匹配是糟糕的。注意,我们假设了所有次优匹配都是糟糕的:
# 准备一个空的掩膜来绘制好的匹配 mask_matches = [[0, 0] for i in range(len(matches))]# 向掩膜中添加数据 for i, (m, n) in enumerate(matches):if m.distance < 0.7 * n.distance:mask_matches[i] = [1, 0]
绘制并显示良好匹配,把mask_matches 列表传递给cv2.drawMatchesKnn 作为可选参数,cv2.drawMatchesKnn 只绘制掩膜中标记为好的匹配(值为1)
img_matches = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None,matchColor=(0, 255, 0), singlePointColor=(255, 0, 0),matchesMask=mask_matches, flags=0)
代码编写
import numpy as np
import cv2
from matplotlib import pyplot as plt# 读入图像
imgname1 = 'E:/qi.png'
imgname2 = 'E:/qiqiqi.png'
img1 = cv2.imread(imgname1)
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 灰度处理图像
img2 = cv2.imread(imgname2)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 灰度处理图像sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)index_params = dict(algorithm=1, tree=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)# 准备一个空的掩膜来绘制好的匹配
mask_matches = [[0, 0] for i in range(len(matches))]# 向掩膜中添加数据
for i, (m, n) in enumerate(matches):if m.distance < 0.7 * n.distance:mask_matches[i] = [1, 0]img_matches = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None,matchColor=(0, 255, 0), singlePointColor=(255, 0, 0),matchesMask=mask_matches, flags=0)cv2.imshow("FLANN", img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到,效果比较理想,几乎所有的匹配项都处于正确的位置。接下来,我们将把这种类型的结果简化为更简洁的几何表示——单应性,他将描述整个匹配对象的姿态,而不是一堆不连续的点。
二、基于FLANN进行单应性匹配
什么是单应性?
平面的单应性被定义为从一个平面到另一个平面的投影映射。
书中的作者(见参考)给出这样的解释:单应性是当一张图是另一张图的一个透视畸变时,在两张图中寻找彼此的一种情况。
FLANN进行单应性匹配流程
- 导入库,读取灰度格式的图像,检测特征并计算SIFT描述符
- 组建一个通过了劳氏比率检验的匹配列表
good_matches = [] for m, n in matches:if m.distance < 0.7 * n.distance:good_matches.append(m)
- 从技术上讲,我们最少可以用4个匹配项来计算单应性。但是,如果这4个匹配项中的任意一个有缺陷,都将会破坏结果的准确性。实际中最少用到10个匹配项。对于额外的匹配项,单应性查找算法可以丢弃一些异常值,以便产生与大部分匹配项子集紧密匹配的结果。因此,我们继续检查是否至少有10个好的匹配项:
MIN_NUM_GOOD_MATCHES = 10 if len(good_matches) >= MIN_NUM_GOOD_MATCHES:
- 如果满足这个条件,那么就查找匹配的关键点的二维坐标,并把这些坐标放入浮点坐标对的两个列表中。一个列表包含查询图像中的关键点坐标,另一个列表包含场景中匹配的关键点坐标:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
寻找单应性:
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)mask_matches = mask.ravel().tolist()
cv2.findHomography参数:
cv2.findHomography(srcPoints, dstPoints, method, ransacReprojThreshold, mask, maxIters, confidence)
· srcPoints:源平面中点的坐标矩阵
· dstPoints:目标平面中点的坐标矩阵
· method:计算单应矩阵所使用的方法。不同的方法对应不同的参数,具体如下:
· 0: 利用所有点的常规方法
· RANSAC:基于RANSAC的鲁棒算法
` LMEDS:最小中值鲁棒算法
· RHO:基于PROSAC的鲁棒算法
· ransacReprojThreshold:将点对视为内点的最大允许重投影错误阈值(仅用于RANSAC和RHO方法)
· mask:可选输出掩码矩阵,通常由鲁棒算法(RANSAC或LMEDS)设置。 请注意,输入掩码矩阵是不需要设置的
· maxIters:RANSAC算法的最大迭代次数,默认值为2000
· confidence:可信度值,取值范围为0到1执行透射转换,取查询图像的矩形角点,并将其投影到场景中,从而画出边界
h, w = img1.shape[:2]src_corners = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst_corners = cv2.perspectiveTransform(src_corners, M)img2 = cv2.polylines(img2, [np.int32(dst_corners)], True, (255, 0, 0), 3, cv2.LINE_AA)
然后,继续绘制关键点并显示可视化效果
代码编写
import numpy as np
import cv2
from matplotlib import pyplot as plt# 读入图像
imgname1 = 'E:/qiqiqi.png'
imgname2 = 'E:/train1.jpg'
img1 = cv2.imread(imgname1)
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 灰度处理图像
img2 = cv2.imread(imgname2)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 灰度处理图像sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)index_params = dict(algorithm=1, tree=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)good_matches = []
for m, n in matches:if m.distance < 0.7 * n.distance:good_matches.append(m)MIN_NUM_GOOD_MATCHES = 10
if len(good_matches) >= MIN_NUM_GOOD_MATCHES:src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)mask_matches = mask.ravel().tolist()h, w = img1.shape[:2]src_corners = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst_corners = cv2.perspectiveTransform(src_corners, M)img2 = cv2.polylines(img2, [np.int32(dst_corners)], True, (255, 0, 0), 3, cv2.LINE_AA)else:mask_matches = Nonedraw_params = dict(matchColor=(0, 255, 0), singlePointColor=None, matchesMask=mask_matches, flags=2)img3 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, **draw_params)cv2.imshow("FLANN", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
【参考】:OpenCV 4计算机视觉 Python语言实现(原书第三版) 作者:Joseph Howse
python+OpenCV笔记(三十五):特征匹配——基于FLANN的匹配、基于FLANN进行单应性匹配相关推荐
- akaze特征匹配怎么去掉不合适的点_单应性矩阵应用基于特征的图像拼接
点击上方蓝字关注我们 微信公众号:OpenCV学堂 关注获取更多计算机视觉与深度学习知识 前言 前面写了一篇关于单应性矩阵的相关文章,结尾说到基于特征的图像拼接跟对象检测中单应性矩阵应用场景.得到很多 ...
- python将图像转换为8位单通道_【图像处理】OpenCV系列三十五--- equalizeHist函数详解...
上一节,我们学习了如何对两个直方图进行比较,看两幅图像的相似度是多少,经过上节的学习,相信大家对compareHist函数已经有了一个清晰的理解,本届呢,我们学习如何对一幅图像进行均衡化! 1.函数原 ...
- OpenCV笔记(十五)——使用Laplace算子进行图像的边缘检测
在笔记十四中,我们使用了Sobel算子对图像进行边缘检测,理论依据是像素变化最快的地方最有可能是边缘处,所以使用sobel算子对图像做微分,得到的结果图像当中灰度较大的区域,即为边缘处. 在这里,我们 ...
- 【Visual C++】游戏开发笔记三十五 站在巨人的肩膀上:游戏引擎导论
看到在留言中很多朋友提到不太清楚DirectX与游戏引擎的区别的问题,在这里浅墨就专门把自己对游戏引擎的一些理解写成一篇文章,作为我们<Visual C++游戏开发>专栏的游戏引擎导论,也 ...
- 游戏开发笔记三十五 站在巨人的肩膀上:游戏引擎导论
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8250057 作者:毛星云(浅墨 ...
- Python学习笔记(十五):类基础
以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-24 23:59 学习笔记 1,Python中的大多 ...
- Python学习笔记整理(十五)类的编写细节
类代码编写细节 一.class语句 一般形式 class <name>(superclass,...): data=value def mothod(self,... ...
- Python+OpenCV图像处理(十五)—— 圆检测
简介: 1.霍夫圆变换的基本原理和霍夫线变换原理类似,只是点对应的二维极径.极角空间被三维的圆心和半径空间取代.在标准霍夫圆变换中,原图像的边缘图像的任意点对应的经过这个点的所有可能圆在三维空间用圆心 ...
- opencv学习(三十五)之仿射变换warpAffine
1.仿射变换介绍 仿射变换是指在向量空间中进行一次线性变换(乘以一个矩阵)并加上一个平移(加上一个向量),变换为另一个向量空间的过程.在有限维的情况下,每个仿射变换可以由一个矩阵A和一个向量b给出,它 ...
最新文章
- 德勤管理咨询热招 | @企业级 IT 大伽和数字化大咖:春风十里不如你!
- Stanford UFLDL教程 自我学习
- 第67课 选择排序 改进例67.1 《小学生C++编程入门》
- [转]Windows Shell 编程 第二章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987893】...
- 【博士后招聘】浙江大学杨杰课题组-医学AI/大数据分析/自然语言处理
- 序列化和反序列化(四)——序列化存储规则
- html宠物医院制作与实现,基于JSP的宠物医院系统设计与实现-毕业设计.doc
- 交叉编译及交叉编译工具链的安装
- NXP ZigBee JN5169 DimmerLight编译过程梳理
- java小程序坦克大战,小程序经典游戏,微信欢乐坦克大战攻略
- 【毕业设计】基于单片机的火灾报警系统 -stm32 单片机物联网
- JAVA中Action层, Service层 ,modle层 和 Dao层的功能区分
- Python中基于TCP网络通信协议的多人聊天室
- 烟台初中计算机会考,山东烟台市2018年初中学业水平考试WORD 版有答案
- 某电商客户数据价值分析项目
- 【小程序云开发】本地调试和云端测试的结果不一致,返回值result为null
- 支持向量机(四)——非线性支持向量机
- E - Mafia CodeForces - 348A (推公式,思维)
- coreldraw sp2精简版 x4_coreldraw x4
- 烤仔观察 | NFT+社交,2021年欧洲杯观赛新“姿势”来啦~速戳!