当前位置: 首页 > news >正文

cv2-特征点匹配(bf、FLANN)

cv2-特征点匹配(bf、KNN、FLANN)

文章目录

  • cv2-特征点匹配(bf、KNN、FLANN)
    • 1. 暴力匹配法(bf)
      • 1.1 bf.match()
      • 1.2 bf.knnMatch()
    • 3. FLANN匹配法
    • 4. 总结

1. 暴力匹配法(bf)

(又称作:交叉匹配)交叉过滤的思想很简单,再进行一次匹配,反过来使用被匹配到的点进行匹配,如果匹配到的仍然是第一次匹配的点的话,就认为这是一个正确的匹配。

eg:假如第一次特征点A使用暴力匹配的方法,匹配到的特征点是特征点B;反过来,使用特征点B进行匹配,如果匹配到的仍然是特征点A,则就认为这是一个正确的匹配,否则就是一个错误的匹配。

cv2.BFMatcher() 创建一个 BFMatcher 对象。它有两个可选参数。

  • 第一个是normType。它是用来指定要使用的距离测试类型。默认值为cv2.Norm_L2。这很适合SIFT和SURF等(c2.NORM_L1 也可以)。对于使用二进制描述符的 ORB,BRIEF,BRISK算法等,要使用 cv2.NORM_HAMMING,这样就会返回两个测试对象之间的汉明距离。如果 ORB 算法的参数设置为 V T A_K==3 或 4,normType就应该设置成 cv2.NORM_HAMMING2。

  • 第二个参数是布尔变量 crossCheck,默认值为False。如果设置为True,匹配条件就会更加严格,只有到 A 中的第 i 个特征点与 B 中的第 j 个特征点距离最近,并且 B 中的第 j 个特征点到 A 中的第 i 个特征点也是最近(A 中没有其他点到 j 的距离更近)时才会返回最佳匹配(i,j)。也就是这两个特征点要互相匹配才行。这样就能提供统一的结果,这可以用来替代 D.Lowe在 SIFT 文章中提出的比值测试方法。

而创建的BFMatcher 对象具有两个方法,BFMatcher.match() 和BFMatcher.knnMatch()。

  • 第一个方法会返回最佳匹配。
  • 第二个方法为每个关键点返回 k 个最佳匹配(降序排列之后取前 k 个),其中 k 是由用户设定的。如果除了匹配之外还要做其他事情的话可能会用上(比如进行比值测试)。

就像使用 cv2.drawKeypoints() 绘制关键点一样,我们可以使用cv2.drawMatches() 来绘制匹配的点。它会将这两幅图像先水平排列,然后在最佳匹配的点之间绘制直线(从原图像到目标图像)。

  • 如果前面使用的是 BFMatcher.knnMatch(),现在我们可以使用函数 cv2.drawMatchsKnn为每个关键点和它的 k 个最佳匹配点绘制匹配线。
  • 如果 k 等于 2,就会为每个关键点绘制两条最佳匹配直线。如果我们要选择性绘制话就要给函数传入一个掩模。

注意,

  • cv2.drawMatches()使用的点对集keypoint是一维的,(N,);
    matchesMask是计算转换H矩阵时生成的掩模,inliers的点,最后只画出符合条件的inliers点对。
  • cv2.drawMatchesKnn()使用的点对集keypoint是一维的,(N,1);
    画出keypoin中前几个点对连线。

1.1 bf.match()

matches = bf.match(des1, des2) 返回值是一个 DMatch 对象列表。这个DMatch 对象具有下列属性:

  • DMatch.distance:描述符之间的距离。越小越好。
  • DMatch.trainIdx : 目标图像中描述符的索引。
  • DMatch.queryIdx :查询图像中描述符的索引。
  • DMatch.imgIdx :目标图像的索引。
detector = cv2.ORB_create()  # Oriented FAST and Rotated BRIEFkp1 = 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)# 利用匹配器 匹配两个描述符的相近程度
'''1. 暴力法'''
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)"""2. knn法"""
matches = bf.knnMatch(des1, des2, k=1)  # knn 匹配可以返回k个最佳的匹配项、bf返回所有的匹配项
img32 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches[: 50], img2, flags=2)
  • 优化:对 matches 匹配点进行排序,提取匹配效果最优的50个点。过滤掉相似性低的特征点。

1.2 bf.knnMatch()

返回对象,同bf.Match()。

K近邻匹配,在匹配的时候选择K个和特征点最相似的点,如果这K个点之间的区别足够大,则选择最相似的那个点作为匹配点,通常选择K = 2,也就是最近邻匹配。对每个匹配返回两个最近邻的匹配,如果第一匹配和第二匹配距离比率足够大(向量距离足够远),则认为这是一个正确的匹配,比率的阈值通常在2左右。

knn匹配过程中很可能发生错误的匹配,错误的匹配主要有两种:

  • 匹配的特征点事错误的
  • 图像上的特征点无法匹配。

KNNMatch():暴力法的基础上添加比率测试。

  • 1)交叉过滤 (暴力法)
    如果第一幅图像的一个特征点和第二幅图像的一个特征点相匹配,则进行一个相反的检查,即将第二幅图像上的特征点与第一幅图像上相应特征点进行匹配,如果匹配成功,则认为这对匹配是正确的。
     
    OpenCV中的BFMatcher已经包含了这种过滤 BFMatcher matcher(NORM_L2,true),在构造BFMatcher是将第二个参数设置为true。

  • 2)比率测试 (knn)
    KNNMatch,可设置K = 2 ,即对每个匹配返回两个最近邻描述符,仅当第一个匹配与第二个匹配之间的距离足够小时,才认为这是一个匹配。

3. FLANN匹配法

FLANN是快速最近邻搜索算法寻找的简称(用快速的第三方库近似最近邻搜索算法)。它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。对于大型数据集,它的运行速度比BFMatcher快。

使用 FLANN 匹配,我们需要传入两个字典作为参数。这两个用来确定要使用的算法和其他相关参数等。

  • 第一个是 IndexParams。各种不同算法的信息可以在 FLANN 文档中找到。这里我们总结一下,对于 SIFT 和 SURF 等,我们可以传入的参数是:
    FLANN_INDEX_KDTREE = 1index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)。

  • 第二个字典是 SearchParams。用它来指定递归遍历的次数。值越高结果越准确,但是消耗的时间也越多。如果你想修改这个值,传入参数:searchparams = dict(checks = 100)。

# 创建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)

4. 总结

特征匹配:FLANN效果更好一些。

相关文章:

cv2-特征点匹配(bf、FLANN)

cv2-特征点匹配&#xff08;bf、KNN、FLANN&#xff09; 文章目录cv2-特征点匹配&#xff08;bf、KNN、FLANN&#xff09;1. 暴力匹配法&#xff08;bf&#xff09;1.1 bf.match()1.2 bf.knnMatch()3. FLANN匹配法4. 总结1. 暴力匹配法&#xff08;bf&#xff09; &#xff08…...

基于matlab多功能相控阵雷达资源管理的服务质量优化

一、前言此示例说明如何为基于服务质量 &#xff08;QoS&#xff09; 优化的多功能相控阵雷达 &#xff08;MPAR&#xff09; 监控设置资源管理方案。它首先定义必须同时调查的多个搜索扇区的参数。然后&#xff0c;它介绍了累积检测范围作为搜索质量的度量&#xff0c;并展示了…...

立创eda专业版学习笔记(6)(pcb板移动节点)

先要看一个设置方面的东西&#xff1a; 进入设置-pcb-通用 我鼠标放到竖着的线上面&#xff0c;第一次点左键是这样选中的&#xff1a; 再点一次左键是这样选中的&#xff1a; 这个时候&#xff0c;把鼠标放到转角的地方&#xff0c;点右键&#xff0c;就会出现对于节点的选项…...

Java面试——MyBatis相关知识

目录 1.什么是MyBatis 2.MyBatis优缺点 3.MyBatis工作原理 4.MyBatis缓存模式 5.MyBatis代码相关问题 6.MyBatis和hibernate区别 1.什么是MyBatis MyBatis是一个半ORM持久层框架&#xff08;对象关系映射&#xff09;&#xff0c;基于JDBC进行封装&#xff0c;使得开发者…...

Cortex-M0编程入门

目录1.嵌入式系统编程入门微控制器是如何启动的嵌入式程序设计2.输入和输出3.开发流程4.C编程和汇编编程5.什么是程序映像6.C编程&#xff1a;数据类型7.用C语言操作外设8.Cortex微控制器软件接口标准&#xff08;CMSIS&#xff09;简介标准化内容组织结构使用方法优势1.嵌入式…...

字符串函数能有什么坏心思?

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;夏目的C语言宝藏 &#x1f4ac;总结&#xff1a;希望你看完之…...

Vue3 组件之间的通信

组件之间的通信 经过前面几章的阅读&#xff0c;相信开发者已经可以搭建一个基础的 Vue 3 项目了&#xff01; 但实际业务开发过程中&#xff0c;还会遇到一些组件之间的通信问题&#xff0c;父子组件通信、兄弟组件通信、爷孙组件通信&#xff0c;还有一些全局通信的场景。 …...

多路查找树

1.二叉树与 B 树 1.1二叉树的问题分析 二叉树的操作效率较高&#xff0c;但是也存在问题, 请看下面的二叉树 二叉树需要加载到内存的&#xff0c;如果二叉树的节点少&#xff0c;没有什么问题&#xff0c;但是如果二叉树的节点很多(比如 1 亿)&#xff0c; 就 存在如下问题:问…...

Mybatis——注入执行sql查询、更新、新增以及建表语句

文章目录前言案例dao和mapper编写XXXmapper.xml编写编写业务层代码&#xff0c;进行注入调用额外扩展--创建表语句前言 在平时的项目开发中&#xff0c;mybatis应用非常广泛&#xff0c;但一般都是直接CRUD类型sql的执行。 本片博客主要说明一个另类的操作&#xff0c;注入sq…...

即时通讯系列-4-如何设计写扩散下的同步协议方案

1. 背景信息 上篇提到了, IM协议层是主要解决会话和消息的同步, 在实现上, 以推模式为主, 拉模式为辅. 本文Agenda: (How)如何同步(How)如何设计同步位点如何设计 Gap过大(SyncGapOverflow) 机制如何设计Ack机制总结 提示: 本系列文章不会单纯的给出结论, 希望能够分享的是&…...

tui-swipe-action组件上的按钮点击后有阴影的解决方法

大家好,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。 目录 前言问题描述问题解决前言 一直未敢涉足电商领域,总觉得这里面的道道很多,又是支付、又是物流的,还涉及到金钱,所以我们所做的项目,一直都是XXXX管理系统,XXX考核系统,移动端的也是,XX健康管理平台…… 但…...

【大数据Hadoop】Hadoop 3.x 新特性总览

Hadoop 3.x 新特性剖析系列11. 概述2. 内容2.1 JDK2.2 EC技术2.3 YARN的时间线V.2服务2.3.1 伸缩性2.3.2 可用性2.3.3 架构体系2.4 优化Hadoop Shell脚本2.5 重构Hadoop Client Jar包2.6 支持等待容器和分布式调度2.7 支持多个NameNode节点2.8 默认的服务端口被修改2.9 支持文件…...

Python-第三天 Python判断语句

Python-第三天 Python判断语句一、 布尔类型和比较运算符1.布尔类型2.比较运算符二、if语句的基本格式1.if 判断语句语法2.案例三、 if else 语句1.语法2.案例四 if elif else语句1.语法五、判断语句的嵌套1.语法六、实战案例一、 布尔类型和比较运算符 1.布尔类型 布尔&…...

失手删表删库,赶紧跑路?!

在数据资源日益宝贵的数字时代公司最怕什么&#xff1f;人还在&#xff0c;库没了是粮库、车库&#xff0c;还是小金库&#xff1f;实际上&#xff0c;这里的“库”是指的数据库Ta是公司各类信息的保险柜小到企业官网和客户信息大到金融机构的资产数据和国家秘密即便没有跟数据…...

技术树基础——16排它平方数(Bigdecimal,int,string,数组的转换)

题目&#xff1a;03879 * 203879 41566646641这有什么神奇呢&#xff1f;仔细观察&#xff0c;203879 是个6位数&#xff0c;并且它的每个数位上的数字都是不同的&#xff0c;并且它平方后的所有数位上都不出现组成它自身的数字。具有这样特点的6位数还有一个&#xff0c;请你…...

04动手实践:手把手带你实现gRPC的Hello World

这篇文章就从实践的角度出发,带大家一起体验一下gRPC的Hello World。文中的代码将全部使用Go语言实现,使用到的示例也是GitHub上提供的grpc-go,下面我们开始: Hello World官方示例 首先我们要clone GitHub上gRPC的源代码到我们本地 git clone https://github.com/grpc/g…...

区块链技术与应用1——BTC-密码学原理

文章目录比特币中的密码学原理1. 哈希函数2. 数字签名3. 比特币中的哈希函数和数字签名简单介绍&#xff1a;比特币与以太坊都是以区块链技术为基础的两种加密货币&#xff0c;因为他们应用最广泛&#xff0c;所以讲区块链技术一般就讲比特币和以太坊。比特币中的密码学原理 1…...

PyTorch学习笔记:data.WeightedRandomSampler——数据权重概率采样

PyTorch学习笔记&#xff1a;data.WeightedRandomSampler——数据权重概率采样 torch.utils.data.WeightedRandomSampler(weights, num_samples, replacementTrue, generatorNone)功能&#xff1a;按给定的权重(概率)[p0,p1,…,pn−1][p_0,p_1,\dots,p_{n-1}][p0​,p1​,…,pn…...

SpringMVC对请求参数的处理

如何获取SpringMVC中请求中的信息 &#xff1f; 默认情况下&#xff0c;可以直接在方法的参数中填写跟请求参数一样的名称&#xff0c;此时会默认接受参 数 &#xff0c;如果有值&#xff0c;直接赋值&#xff0c;如果没有&#xff0c;那么直接给空值 。Controller RequestMapp…...

12年老外贸的经验分享

回想这12年的经历&#xff0c;很庆幸自己的三观一直是正确的&#xff0c;就是买家第一不管什么原因&#xff0c;只要你想退货&#xff0c;我都可以接受退款。不能退给上级供应商&#xff0c;我就自己留着&#xff0c;就是为了避免因为这个拒收而失去买家。不管是什么质量原因&a…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...