目标

在本章中,

  • 我们将看到如何将一个图像中的特征与其他图像进行匹配。
  • 我们将在OpenCV中使用Brute-Force匹配器和FLANN匹配器

Brute-Force匹配器的基础

蛮力匹配器很简单。它使用第一组中一个特征的描述符,并使用一些距离计算将其与第二组中的所有其他特征匹配。并返回最接近的一个。

对于BF匹配器,首先我们必须使用cv.BFMatcher()创建BFMatcher对象。

它需要两个可选参数。第一个是normType,它指定要使用的距离测量。默认情况下为cv.NORM_L2。对于SIFT,SURF等(也有cv.NORM_L1)很有用。

对于基于二进制字符串的描述符,例如ORB,BRIEF,BRISK等,应使用cv.NORM_HAMMING,该函数使用汉明距离作为度量。如果ORB使用WTA_K == 34,则应使用cv.NORM_HAMMING2

第二个参数是布尔变量,即crossCheck,默认情况下为false。如果为true,则Matcher仅返回具有值(i,j)的那些匹配项,以使集合A中的第i个描述符具有集合B中的第j个描述符为最佳匹配,反之亦然。即两组中的两个特征应彼此匹配。它提供了一致的结果,并且是D.Lowe在SIFT论文中提出的比率测试的良好替代方案。

创建之后,两个重要的方法是BFMatcher.match()和BFMatcher.knnMatch()。第一个返回最佳匹配。第二种方法返回k个最佳匹配,其中k由用户指定。当我们需要对此做其他工作时,它可能会很有用。

就像我们使用cv.drawKeypoints()绘制关键点一样,cv.drawMatches()可以帮助我们绘制匹配项。它水平堆叠两张图像,并绘制从第一张图像到第二张图像的线,以显示最佳匹配。还有cv.drawMatchesKnn绘制所有k个最佳匹配。如果k=2,它将为每个关键点绘制两条匹配线。因此,如果要选择性地绘制,则必须通过掩码。

让我们来看一个SIFT和ORB的示例(两者都使用不同的距离测量)。

使用ORB描述符进行Brute-Force匹配

在这里,我们将看到一个有关如何在两个图像之间匹配特征的简单示例。在这种情况下,我有一个queryImage和trainImage。我们将尝试使用特征匹配在trainImage中找到queryImage。(图像是/samples/data/box.png和/samples/data/box_in_scene.png)

我们正在使用ORB描述符来匹配特征。因此,让我们从加载图像,查找描述符等开始。

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # 索引图像
img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # 训练图像
# 初始化ORB检测器
orb = cv.ORB_create()
# 基于ORB找到关键点和检测器
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

接下来,我们创建一个距离测量值为cv.NORM_HAMMING的BFMatcher对象(因为我们使用的是ORB),并且启用了CrossCheck以获得更好的结果。然后,我们使用Matcher.match()方法来获取两个图像中的最佳匹配。我们按照距离的升序对它们进行排序,以使最佳匹配(低距离)排在前面。然后我们只抽出前10的匹配(只是为了提高可见度。您可以根据需要增加它)

# 创建BF匹配器的对象
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True) # 匹配描述符.
matches = bf.match(des1,des2) # 根据距离排序
matches = sorted(matches, key = lambda x:x.distance) # 绘制前10的匹配项
img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:10],None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) plt.imshow(img3),plt.show()

将获得以下的结果:

什么是Matcher对象?

matchs = bf.match(des1,des2)行的结果是DMatch对象的列表。该DMatch对象具有以下属性:

  • DMatch.distance-描述符之间的距离。越低越好。
  • DMatch.trainIdx-火车描述符中的描述符索引
  • DMatch.queryIdx-查询描述符中的描述符索引
  • DMatch.imgIdx-火车图像的索引。

带有SIFT描述符和比例测试的Brute-Force匹配

这次,我们将使用BFMatcher.knnMatch()获得k个最佳匹配。在此示例中,我们将k = 2,以便可以应用D.Lowe在他的论文中阐述的比例测试。

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # 索引图像
img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # 训练图像
# 初始化SIFT描述符
sift = cv.xfeatures2d.SIFT_create()
# 基于SIFT找到关键点和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# 默认参数初始化BF匹配器
bf = cv.BFMatcher()
matches = bf.knnMatch(des1,des2,k=2)
# 应用比例测试
good = []
for m,n in matches:
if m.distance < 0.75*n.distance:
good.append([m])
# cv.drawMatchesKnn将列表作为匹配项。
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3),plt.show()

查看以下结果:

基于匹配器的FLANN

FLANN是近似最近邻的快速库。它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。对于大型数据集,它的运行速度比BFMatcher快。我们将看到第二个基于FLANN的匹配器示例。

对于基于FLANN的匹配器,我们需要传递两个字典,这些字典指定要使用的算法,其相关参数等。第一个是IndexParams。对于各种算法,要传递的信息在FLANN文档中进行了说明。概括来说,对于SIFT,SURF等算法,您可以通过以下操作:

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, # 12
key_size = 12, # 20
multi_probe_level = 1) #2

第二个字典是SearchParams。它指定索引中的树应递归遍历的次数。较高的值可提供更好的精度,但也需要更多时间。如果要更改值,请传递search_params = dict(checks = 100)

有了这些信息,我们就很容易了。

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # 索引图像
img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # 训练图像
# 初始化SIFT描述符
sift = cv.xfeatures2d.SIFT_create()
# 基于SIFT找到关键点和描述符
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(checks=50) # 或传递一个空字典
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)
# 只需要绘制好匹配项,因此创建一个掩码
matchesMask = [[0,0] for i in range(len(matches))]
# 根据Lowe的论文进行比例测试
for i,(m,n) in enumerate(matches):
if m.distance < 0.7*n.distance:
matchesMask[i]=[1,0]
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = matchesMask,
flags = cv.DrawMatchesFlags_DEFAULT)
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)
plt.imshow(img3,),plt.show()

查看以下结果:

欢迎关注磐创博客资源汇总站:

http://docs.panchuang.net/

欢迎关注PyTorch官方中文教程站:

http://pytorch.panchuang.net/

OpenCV中文官方文档:

http://woshicver.com/

OpenCV-Python 特征匹配 | 四十四的更多相关文章

  1. OpenCV开发笔记(六十四):红胖子8分钟带你深入了解SURF特征点(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  2. Python学习笔记(十四)

    Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...

  3. 孤荷凌寒自学python第四十四天Python操作 数据库之准备工作

     孤荷凌寒自学python第四十四天Python操作数据库之准备工作 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天非常激动地开始接触Python的数据库操作的学习了,数据库是系统化设计 ...

  4. python自动华 (十四)

    Python自动化 [第十四篇]:HTML介绍 本节内容: Html 概述 HTML文档 常用标签 2. CSS 概述 CSS选择器 CSS常用属性 1.HTML 1.1概述 HTML是英文Hyper ...

  5. 网站开发进阶(四十四)input type="submit" 和"button"的区别

    网站开发进阶(四十四)input type="submit" 和"button"的区别   在一个页面上画一个按钮,有四种办法: 这就是一个按钮.如果你不写ja ...

  6. NeHe OpenGL教程 第四十四课:3D光晕

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. Gradle 1.12用户指南翻译——第四十四章. 分发插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  8. SQL注入之Sqli-labs系列第四十一关(基于堆叠注入的盲注)和四十二关四十三关四十四关四十五关

    0x1普通测试方式 (1)输入and1=1和and1=2测试,返回错误,证明存在注入 (2)union select联合查询 (3)查询表名 (4)其他 payload: ,( ,( 0x2 堆叠注入 ...

  9. “全栈2019”Java第四十四章:继承

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  10. Android项目实战(四十四):Zxing二维码切换横屏扫描

    原文:Android项目实战(四十四):Zxing二维码切换横屏扫描 Demo链接 默认是竖屏扫描,但是当我们在清单文件中配置横屏显示的时候: <activity android:name=&q ...

随机推荐

  1. APM监控工具Pinpoint搭建

    一:Pinpoint简介与架构图示 Pinpoint是什么Pinpoint是开源在github上的一款使用JAVA语言编写的APM监控工具,用于大规模分布式系统监控.它对性能的影响较小,只增加约3%的 ...

  2. @JsonFormat、@DateTimeFormat注解,读取数据库晚一天问题

    @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss&qu ...

  3. LeetCode 153.Find Minimum in Rotated Sorted Array(M)(P)

    题目: Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. ( ...

  4. MongoDB Compass最新版(v_1.20.5)远程连接数据库

    最近下载了最新版本的MongoDB Compass(v_1.20.5)后才发现软件较之前的版本有了很大的变化,主要体现在创建连接页面和连接方式上. 这是旧版的连接页面,所有的参数项以表单的形式列出,直 ...

  5. C# 视频监控系统(提供源码分享)

    去过工厂或者仓库的都知道,在工厂或仓库里面,会有很多不同的流水线,大部分的工厂或仓库,都会在不同流水线的不同工位旁边安装一台电脑,一方面便于工位上的师傅把产品的重要信息录入系统,便于公司系统数据采集分 ...

  6. TP5使用Redis处理电商秒杀

    本篇文章介绍了ThinkPHP使用Redis实现电商秒杀的处理方法,具有一定的参考价值,希望对学习ThinkPHP的朋友有帮助! TP5使用Redis处理电商秒杀 1.首先在TP5中创建抢购活动所需要 ...

  7. go源码分析(四) net包获取主机ip 子网掩码相关分析

    获取本地的ip时 顺便学习了下标准库net中的实现 在net/interface.go中进行了入口调用,返回值为Addr的slice func InterfaceAddrs() ([]Addr, er ...

  8. js实践篇:例外处理Try{}catch(e){}

    本文转载至:http://blog.csdn.net/ocean20/article/details/7301008 程序开发中,编程人员经常要面对的是如何编写代码来响应错误事件的发生,即例外处理(e ...

  9. Elasticsearch系列---多字段搜索

    概要 本篇介绍一下multi_match的best_fields.most_fields和cross_fields三种语法的场景和简单示例. 最佳字段 bool查询采取"more-match ...

  10. java -输入年龄判断是否符合范围。

    //创建的一个包名. package demo3; //定义一个类. public class Test { //公共静态的主方法. public static void main(String[] ...