Python OpenCV特征检测之特征匹配方式详解
作者:机器视觉小学徒 发布时间:2021-07-20 00:51:58
前言 
获得图像的关键点后,可通过计算得到关键点的描述符。关键点描述符可用于图像的特征匹配。通常,在计算图A是否包含图B的特征区域时,将图A称做训练图像,将图B称为查询图像。图A的关键点描述符称为训练描述符,图B的关键点描述符称为查询描述符。
一、暴力匹配器
暴力匹配器使用描述符进行特征比较。在比较时,暴力匹配器首先在查询描述符中取一个关键点的描述符,将其与训练描述符中的所有关键点描述符进行比较,每次比较后会给出一个距离值,距离最小的值对应最佳匹配结果。所有描述符比较完后,匹配器返回匹配结果列表。
OpenCV的cv2.BFMatcher_create()函数用于创建暴力匹配器,其基本格式如下:
bf = cv2.BFMatcher_create([normType[, crossCheck]])
bf
为返回的暴力匹配器对象
normType
为距离测量类型, 默认为cv2.NORM_L2, 通常, SIFT描述符使用cv2.NORM_L1或cv2.NORM_L2, ORB描述符使用cv2.NORM_HAMMING
crossCheck
默认为False, 匹配器为每个查询描述符找到k个距离最近的匹配描述符, 为True时, 只返回满 * 叉验证条件的匹配结果
暴力匹配器对象的match()方法返回每个关键点的最佳匹配结果,其基本格式如下:
ms = bf.match(des1, des2)
ms
为返回的结果, 它是一个DMatch对象列表, 每个DMatch对象表示关键点的一个匹配结果, 其dintance属性表示距离, 距离值越小匹配度越高
des1
为查询描述符
des2
为训练描述符
获得匹配结果后,可调用cv2.drawMatches()函数或cv2.drawMatchesKnn()函数绘制匹配结果图像,其基本格式如下:
outImg = cv2.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2[, matchColor[, singlePointColor[, matchesMask[, flags]]]])
outImg
为返回的绘制结果图像, 图像中查询图像与训练图像中匹配的关键点个两点之间的连线为彩色
img1
为查询图像
keypoints1
为img1的关键点
img2
为训练图像
keypoints2
为img2的关键点
matches1to2
为img1与img2的匹配结果
matchColor
为关键点和链接线的颜色, 默认使用随机颜色
singlePointColor
为单个关键点的颜色, 默认使用随机颜色
matchesMask
为掩膜, 用于决定绘制哪些匹配结果, 默认为空, 表示绘制所有匹配结果
flags
为标志, 可设置为下列参数值:
cv2.DrawMatchesFlags_DEFAUL
:默认方式, 绘制两个源图像、匹配项和单个关键点, 没有围绕关键点的圆以及关键点的大小和方向
cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
:不会绘制单个关键点
cv2.DrawMatchesFlags_DRAW_RICH_KEYPOINTS
:在关键点周围绘制具有关键点大小和方向的圆圈
# 暴力匹配器、ORB描述符和match()方法
import cv2
img1 = cv2.imread("xhu1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("xhu2.jpg", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck = False)
ms = bf.match(des1, des2)
ms = sorted(ms, key = lambda x:x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms[:20], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow("Xhu1", img1)
cv2.imshow("Xhu2", img2)
cv2.imshow("Matches", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 暴力匹配器、SIFT描述符和match()方法
import cv2
img1 = cv2.imread("xhu1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("xhu2.jpg", cv2.IMREAD_GRAYSCALE)
sift=cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck = False)
ms = bf.match(des1, des2)
ms = sorted(ms, key = lambda x:x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms[:20], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# ms = np.expand_dims(ms,1)
# img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, ms[:20], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow("Xhu1", img1)
cv2.imshow("Xhu2", img2)
cv2.imshow("Matches", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
暴力匹配器对象的knnMatch()方法可返回指定数量的最佳匹配结果,其基本格式如下:
ms = knnMatch(des1, des2, k=n)
ms
为返回的匹配结果, 每个列表元素是一个子列表, 它包含了由参数k指定个数的DMatch对象
des1
为查询描述符
des2
为训练描述符
k
为返回的最佳匹配个数
# 暴力匹配器、ORB描述符和knnMatch()方法
import cv2
img1 = cv2.imread("xhu1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("xhu2.jpg", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck = False)
ms = bf.knnMatch(des1, des2, k=2)
# 应用比例测试选择要使用的匹配结果
good = []
for m, n in ms:
if m.distance < 0.75 * n.distance:
good.append(m)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good[:20], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# good = np.expand_dims(good,1)
#img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good[:20], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow("Xhu1", img1)
cv2.imshow("Xhu2", img2)
cv2.imshow("Matches", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
二、FLANN匹配器
FLANN(Fast Library for Approximate Nearest Neignbors)为近似最近邻的快速库,FLANN特征匹配算法比其它的最近邻算法更快。
在创建FLANN匹配器时,需要传递两参数:index_params和search_params。
index_params用来指定索引树的算法类型和数量。SIFT算法可以使用下面的代码来设置。
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,
key_size = 12,
multi_probe_level = 1)
search_params用于指定索引树的遍历次数,遍历次数越多,匹配结果越精细,通常设置为50即可,如下所示:
search_params = dict(check = 50)
# FLANN匹配器、ORB描述符
import cv2
img1 = cv2.imread("xhu1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("xhu2.jpg", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 定义FLANN参数
FLANN_INDEX_LSH = 6
index_params = dict(algorithm = FLANN_INDEX_LSH,
table_number = 6,
key_size = 12,
multi_probe_level = 1)
search_params = dict(check = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.match(des1, des2)
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = None,
flags = cv2.DrawMatchesFlags_DEFAULT)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None, **draw_params)
cv2.imshow("Xhu1", img1)
cv2.imshow("Xhu2", img2)
cv2.imshow("Matches", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
# FLANN匹配器、SIFT描述符
import cv2
img1 = cv2.imread("xhu1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("xhu2.jpg", cv2.IMREAD_GRAYSCALE)
sift = cv2.SIFT_create()
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(check = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.match(des1, des2)
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = None,
flags = cv2.DrawMatchesFlags_DEFAULT)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None, **draw_params)
cv2.imshow("Xhu1", img1)
cv2.imshow("Xhu2", img2)
cv2.imshow("Matches", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
来源:https://blog.csdn.net/weixin_43843069/article/details/122243866


猜你喜欢
- 本文实例讲述了Python使用scipy模块实现一维卷积运算。分享给大家供大家参考,具体如下:一 介绍signal模块包含大量滤波函数、 *
- Python 下的单例模式要点:1.某个类只能有一个实例;2.它必须自行创建这个实例;3.它必须自行向整个系统提供这个实例方法:重写new函
- 程序运算时往往需要数据,而数据的IO又往往需要时间传输,而常见的串行处理,是一个任务处理完成才接着处理新的任务, 其效率低下可想而知。 假如
- 在停止和开始进度条的同时,将进度条清空的动作也是常常需要用到的。具体用法如下:self.progressBar = QProgressBar
- 事务是由一步或几步数据库操作序列组成逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。程序和事务是两个不同的概念。一般而言:一段程序中
- Taglib指令介绍Taglib指令,其实就是定义一个标签库以及自定义标签的前缀。比如struts中支持的标签库,html标签库、bean标
- 经常遇到百度网盘的压缩文件加密了,今天我们就破解它!实现思路上篇文章给大家介绍了爆破密码的思路,感兴趣的朋友可以了解下。其实都大同小异:无非
- 一、代理模式代理模式,为其他对象提供一种代理,以此控制一个对象的访问方式。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象
- 前记上一遍文章《Python中Async语法协程的实现》介绍了Python是如何以生成器来实现协程的以及Python Asyncio通过Fu
- 我们都知道代码都是顺序执行的,也就是先执行第1条语句,然后是第2条、第3条……一直到最后一条语句
- import os ## for os.path.isfile()def dealline(line) :  
- Asyncio 任务可以通过调用它们的 cancel() 方法来取消。我们可以通过将任务包装在对 asyncio.shield() 的调用中
- 本程序有两文件test.asp 和tree.asp 还有一些图标文件 1。test.asp 调用类生成树 代码如下<%@
- 由于javascript是一种无类型语言,所以一个数组的元素可以具有任意的数据类型,同一个数组的不同元素可以具有不同的类型,数组的元素设置可
- 学在前面从本篇博客起,我们将实际完成几个小案例,第一个就是银行卡号识别,预计本案例将写 5 篇左右的博客才可以完成,一起加油吧。本文的目标是
- 图片按钮是我们经常应用的网页元素,按钮的生成有两种方法,一个是用链接<a>来模拟按钮,一个是用现成的表单按钮。<input
- 什么是继承啊?答:别人白给你的过程就叫继承。为什么要用继承呢?答:捡现成的呗。好吧,既然大家都想捡现成的,那就要学会怎么继承!在了解之前,你
- 本文实例为大家分享了python实现教务管理系统,供大家参考,具体内容如下mysql+python构成教务管理系统,提供系统管理员,教职工,
- 本文实例讲述了Python实现的ftp服务器功能。分享给大家供大家参考,具体如下:python 具备强大的网络编程功能,而且代码简介,用简单
- 环境cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [root@l