《OpenCV》——特征提取与匹配方法

news2025/2/21 9:27:52

特征提取

特征提取是从原始数据中提取出能够代表数据本质特征和关键信息的过程,在很多领域都有广泛应用。原始数据往往包含大量的冗余信息,特征提取的目的是去除这些冗余,提取出最具代表性、最能区分不同类别或模式的特征,从而降低数据维度,提高数据处理的效率和效果,同时也有助于后续的数据分析、建模和决策。

  • 类别
    • SIFT(尺度不变特征变换):虽然 SIFT 算法存在专利限制,但在非商业用途中仍可在 OpenCV 中使用。通过构建高斯金字塔寻找尺度空间极值点,为关键点分配方向,生成 128 维描述子。
    • SURF(加速稳健特征):SURF 是 SIFT 的加速版本,同样具有尺度和旋转不变性。它利用积分图像快速计算特征点和描述子,计算效率比 SIFT 更高。
    • ORB(加速分割测试的定向 FAST 和旋转 BRIEF):ORB 是一种快速的特征提取和描述算法,它结合了 FAST(Features from Accelerated Segment Test)关键点检测和 BRIEF(Binary Robust Independent Elementary Features)描述子,并对其进行了改进。ORB 具有计算速度快、实时性好的优点,同时在一定程度上也具有旋转和尺度不变性。
    • BRISK(二进制稳健不变可伸缩关键点):BRISK 也是一种二进制描述子算法,它通过在不同尺度上采样点对来计算描述子,具有较好的尺度和旋转不变性,同时对噪声也有一定的鲁棒性。

这些特征提取算法在不同的应用场景中各有优劣,本文主要介绍sift算法。

特征提取sift算法

SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)是一种计算机视觉领域中非常重要的特征提取算法,由加拿大教授 David Lowe 于 1999 年提出,并在 2004 年加以完善总结。以下是关于 SIFT 算法的详细介绍:

在这里插入图片描述

  • 算法原理
    • 尺度空间极值检测:SIFT 算法首先使用不同尺度的高斯滤波器对图像进行卷积操作,生成高斯金字塔。然后在高斯金字塔的不同层之间进行比较,寻找图像中的极值点(极大值或极小值),这些极值点就是可能的特征点位置。通过这种方式,使得算法能够检测到不同尺度下的特征,从而实现尺度不变性。
      在这里插入图片描述

    • 关键点定位:在找到极值点后,需要进一步确定这些点是否为真正的关键点。通过拟合三维二次函数来精确确定关键点的位置和尺度,同时去除低对比度的点和位于边缘上的点,以提高关键点的稳定性和可靠性。

    • 方向赋值:为每个关键点指定一个方向,使得算法具有旋转不变性。通过计算关键点邻域内的梯度方向直方图,将直方图中峰值对应的方向作为关键点的主方向,同时也可以根据需要确定几个辅方向。

    • 关键点描述:在确定了关键点的位置、尺度和方向后,以关键点为中心取一个邻域窗口,将该窗口内的像素点的梯度方向和幅值进行统计,生成一个描述子。通常使用一个 128 维的向量来描述每个关键点,这个描述子包含了关键点周围区域的丰富信息,具有很强的区分性。

  • 优点
    • 尺度和旋转不变性:SIFT 特征对图像的尺度变化和旋转变换具有很好的不变性,能够在不同尺度和角度下准确地检测到相同的特征。
    • 光照不变性:通过对梯度信息的利用,SIFT 特征对光照变化也具有一定的鲁棒性,在不同的光照条件下仍然能够保持较好的性能。
    • 独特性高:生成的 128 维描述子具有很高的独特性,能够有效地区分不同的特征点,适用于各种复杂的图像匹配和识别任务。
  • 缺点
    • 计算复杂度高:SIFT 算法涉及到高斯金字塔的构建、极值点检测、方向赋值和描述子生成等多个步骤,计算量较大,运行速度相对较慢,不适合对实时性要求较高的应用场景。
    • 专利问题:由于 SIFT 算法受专利保护,在商业应用中需要获得相应的授权,这在一定程度上限制了它的广泛应用。
  • 应用领域
    • 图像匹配:SIFT 特征常用于图像之间的匹配,通过找到两幅图像中特征点的对应关系,可以实现图像的对齐、拼接等操作,例如全景图像拼接。
    • 目标识别:在目标识别任务中,SIFT 特征可以用于提取目标物体的特征,与数据库中的模板进行匹配,从而识别出目标物体,如在安防监控中识别特定的目标物体。
    • 三维重建:通过对多幅图像中特征点的匹配和分析,可以计算出相机的位置和姿态,进而实现场景的三维重建,如在文物数字化保护中对文物进行三维建模。

匹配方法

在 OpenCV 中,提供了多种特征匹配方法,主要可分为基于传统特征的匹配和基于深度学习的匹配:

基于传统特征的匹配方法
  • 暴力匹配(Brute-Force Matcher,BFMatcher)

    • 原理:对一个图像中的每个特征描述符,在另一个图像的所有特征描述符中进行遍历,计算它们之间的距离(对于 SIFT、SURF 等浮点型描述符常用欧氏距离,对于 ORB、BRIEF 等二进制描述符常用汉明距离),选择距离最小的作为匹配项。
      在这里插入图片描述
  • FLANN 匹配(Fast Library for Approximate Nearest Neighbors)

    • 原理:FLANN 是一个用于快速近似最近邻搜索的库。它采用了诸如 KD 树、层次聚类树等高效的数据结构和算法,在处理大规模特征点时,比暴力匹配速度更快。
      在这里插入图片描述

本文主要讲解FLANN 匹配

FLANN 匹配

FLANN(Fast Library for Approximate Nearest Neighbors)是一个用于快速近似最近邻搜索的开源库,在 OpenCV 中被广泛用于特征匹配任务。在处理大规模特征点时,传统的暴力匹配方法(Brute - Force Matching)计算量极大、效率低下,而 FLANN 通过使用高效的数据结构和算法,能够在可接受的精度损失下,显著提高匹配速度。

  • 原理
    • FLANN 采用了多种近似最近邻搜索算法,常见的有 KD 树(KD - Tree)和层次聚类树(Hierarchical Clustering Tree)等:
      • KD 树(KD - Tree):是一种对 k 维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。它通过不断地在不同维度上进行划分,将高维空间分割成多个区域,从而在搜索最近邻时可以减少不必要的比较,提高搜索效率。
      • 层次聚类树(Hierarchical Clustering Tree):它将数据点进行层次聚类,构建成一棵树形结构。在搜索时,从树的根节点开始,根据数据点与聚类中心的距离逐步向下搜索,快速缩小搜索范围。

实例

对指纹进行识别:

对十张指纹图片分别和模版指纹图片进行匹配,打印两个指纹间匹配点的个数。、

模版图片:
在这里插入图片描述

十张指纹图片:
在这里插入图片描述

实例过程

设置计算两个指纹间匹配点的个数的函数
"""====================计算两个指纹间匹配点的个数==============="""
def getNum(src, model):
    # 读取源指纹图像
    img1 = cv2.imread(src)
    # 读取模板指纹图像
    img2 = cv2.imread(model)
    # 创建SIFT(尺度不变特征变换)特征检测器对象
    sift = cv2.SIFT_create()
    # 检测并计算源图像的关键点和描述符
    # kp1 是关键点列表,des1 是对应的描述符矩阵
    kp1, des1 = sift.detectAndCompute(img1, None)
    # 检测并计算模板图像的关键点和描述符
    # kp2 是关键点列表,des2 是对应的描述符矩阵
    kp2, des2 = sift.detectAndCompute(img2, None)
    # 创建基于FLANN(快速最近邻搜索包)的匹配器对象
    flann = cv2.FlannBasedMatcher()
    # 使用knnMatch方法进行特征匹配,返回每个查询描述符的前k个最近邻匹配结果
    # k=2 表示为每个查询描述符找到两个最近邻匹配
    matches = flann.knnMatch(des1, des2, k=2)
    # 初始化一个空列表,用于存储满足筛选条件的匹配点
    ok = []
    # 遍历所有匹配结果
    for m, n in matches:
        # 根据比值测试筛选匹配点
        # 如果第一个匹配的距离小于第二个匹配距离的0.8倍,则认为是好的匹配
        if m.distance < 0.8 * n.distance:
            ok.append(m)
    # 统计满足条件的匹配点的数量
    num = len(ok)
    return num
获取指纹编号
'''===================获取指纹编号==============='''
def getID(src, database):
    # 初始化最大匹配点数为0
    max = 0
    # 遍历数据库文件夹中的所有文件
    for file in os.listdir(database):
        # 拼接数据库中文件的完整路径
        model = os.path.join(database, file)
        # 调用getNum函数计算源指纹图像与当前模板指纹图像的匹配点数
        num = getNum(src, model)
        # 打印文件名和对应的匹配点数
        print('文件名:', file, '匹配数字:', num)
        # 如果当前匹配点数大于之前记录的最大匹配点数
        if num > max:
            # 更新最大匹配点数
            max = num
            # 记录当前文件名
            name = file
    # 从文件名中提取指纹编号,取文件名的第一个字符
    ID = name[0]
    # 如果最大匹配点数小于100,认为没有找到匹配的指纹,将编号设为9999
    if max < 100:
        ID = 9999
    return ID
根据指纹编号,获取对应名字
def getName(ID):
    # 定义一个字典,将指纹编号映射到对应的人名
    nameID = {0: '张三', 1: '李四', 2: '王五', 3: '赵六', 4: '朱老七', 5: '钱八',
              6: '曹九', 7: '王二麻子', 8: 'andy', 9: 'Anna', 9999: '没找到'}
    # 根据指纹编号从字典中获取对应的人名
    name = nameID.get(int(ID))
    return name
调用函数进行指纹匹配
if __name__ == "__main__":
    # 定义源指纹图像的文件名
    src = 'src.bmp'
    # 定义存储指纹模板的数据库文件夹名
    database = 'database'
    # 调用getID函数获取指纹编号
    ID = getID(src, database)
    # 调用getName函数根据指纹编号获取对应的人名
    name = getName(ID)
    # 打印识别结果
    print('识别结果为:', name)
结果

在这里插入图片描述

完整代码
import os
import cv2

"""====================计算两个指纹间匹配点的个数==============="""
def getNum(src, model):
    # 读取源指纹图像
    img1 = cv2.imread(src)
    # 读取模板指纹图像
    img2 = cv2.imread(model)
    # 创建SIFT(尺度不变特征变换)特征检测器对象
    sift = cv2.SIFT_create()
    # 检测并计算源图像的关键点和描述符
    # kp1 是关键点列表,des1 是对应的描述符矩阵
    kp1, des1 = sift.detectAndCompute(img1, None)
    # 检测并计算模板图像的关键点和描述符
    # kp2 是关键点列表,des2 是对应的描述符矩阵
    kp2, des2 = sift.detectAndCompute(img2, None)
    # 创建基于FLANN(快速最近邻搜索包)的匹配器对象
    flann = cv2.FlannBasedMatcher()
    # 使用knnMatch方法进行特征匹配,返回每个查询描述符的前k个最近邻匹配结果
    # k=2 表示为每个查询描述符找到两个最近邻匹配
    matches = flann.knnMatch(des1, des2, k=2)
    # 初始化一个空列表,用于存储满足筛选条件的匹配点
    ok = []
    # 遍历所有匹配结果
    for m, n in matches:
        # 根据比值测试筛选匹配点
        # 如果第一个匹配的距离小于第二个匹配距离的0.8倍,则认为是好的匹配
        if m.distance < 0.8 * n.distance:
            ok.append(m)
    # 统计满足条件的匹配点的数量
    num = len(ok)
    return num

'''===================获取指纹编号==============='''
def getID(src, database):
    # 初始化最大匹配点数为0
    max = 0
    # 遍历数据库文件夹中的所有文件
    for file in os.listdir(database):
        # 拼接数据库中文件的完整路径
        model = os.path.join(database, file)
        # 调用getNum函数计算源指纹图像与当前模板指纹图像的匹配点数
        num = getNum(src, model)
        # 打印文件名和对应的匹配点数
        print('文件名:', file, '匹配数字:', num)
        # 如果当前匹配点数大于之前记录的最大匹配点数
        if num > max:
            # 更新最大匹配点数
            max = num
            # 记录当前文件名
            name = file
    # 从文件名中提取指纹编号,取文件名的第一个字符
    ID = name[0]
    # 如果最大匹配点数小于100,认为没有找到匹配的指纹,将编号设为9999
    if max < 100:
        ID = 9999
    return ID

'''======================根据指纹编号,获取对应名字==================='''
def getName(ID):
    # 定义一个字典,将指纹编号映射到对应的人名
    nameID = {0: '张三', 1: '李四', 2: '王五', 3: '赵六', 4: '朱老七', 5: '钱八',
              6: '曹九', 7: '王二麻子', 8: 'andy', 9: 'Anna', 9999: '没找到'}
    # 根据指纹编号从字典中获取对应的人名
    name = nameID.get(int(ID))
    return name

'''==================主函数=========================='''
if __name__ == "__main__":
    # 定义源指纹图像的文件名
    src = 'src.bmp'
    # 定义存储指纹模板的数据库文件夹名
    database = 'database'
    # 调用getID函数获取指纹编号
    ID = getID(src, database)
    # 调用getName函数根据指纹编号获取对应的人名
    name = getName(ID)
    # 打印识别结果
    print('识别结果为:', name)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用DHTMLX Scheduler的拖放功能,在 JS 日程安排日历中创建一组相同的事件

DHTMLX Scheduler 是一个全面的调度解决方案&#xff0c;涵盖了与规划事件相关的广泛需求。假设您在我们的 Scheduler 文档中找不到任何功能&#xff0c;并且希望在我们的 Scheduler 文档中看到您的项目。在这种情况下&#xff0c;很可能可以使用自定义解决方案来实现此类功能。…

​矩阵元素的“鞍点”​

题意&#xff1a; 一个矩阵元素的“鞍点”是指该位置上的元素值在该行上最大、在该列上最小。 本题要求编写程序&#xff0c;求一个给定的n阶方阵的鞍点。 输入格式&#xff1a; 输入第一行给出一个正整数n&#xff08;1≤n≤6&#xff09;。随后n行&#xff0c;每行给出n个整数…

Qt的isVisible ()函数介绍和判断窗口是否在当前界面显示

1、现象&#xff1a;当Qt的窗口最小化时&#xff0c;isVisible值一定是true&#xff0c;这是正常的。 解释&#xff1a;在Qt中&#xff0c;当你点击窗口的最小化按钮时&#xff0c;Qt内部不会自动调用 hide() 方或 setVisible(false) 来隐藏窗口。相反&#xff0c;它会改变窗口…

Unity-Mirror网络框架-从入门到精通之LagCompensation示例

文章目录 前言什么是滞后补偿Lag Compensation示例延迟补偿原理ServerCubeClientCubeCapture2DSnapshot3D补充LagCompensation.cs 独立算法滞后补偿器组件注意:算法最小示例前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mir…

Jenkins 通过 Execute Shell 执行 shell 脚本 七

Jenkins 通过 Execute Shell 执行 shell 脚本 七 一、创建 .sh 文件 项目目录下新建 .sh 文件 jenkins-script\shell\ci_android_master.sh添加 Execute Shell 模块 在 Command 中添加 # 获取 .sh 路径 CI_ANDROID_MASTER_PATH"${WORKSPACE}/jenkins-script/shell/…

PyCharm 批量替换

选择替换的内容 1. 打开全局替换窗口 有两种方式可以打开全局替换窗口&#xff1a; 快捷键方式&#xff1a; 在 Windows 或 Linux 系统下&#xff0c;按下 Ctrl Shift R。在 Mac 系统下&#xff0c;按下 Command Shift R。菜单操作方式&#xff1a;点击菜单栏中的 Edit&…

Linux-文件基本操作

1.基本概念 文件: 一组相关数据的集合 文件名: 01.sh //文件名 2.linux下的文件类型 b block 块设备文件 eg: 硬盘 c character 字符设备文件 eg: 鼠标&#xff0c;键盘 d directory 目录文件 eg: 文件夹 - regular 常规文件…

Oracle临时表空间(基础操作)

临时表空间 临时表空间&#xff1a;用来存放用户的临时数据&#xff0c;临时数据在需要时被覆盖&#xff0c;关闭数据库后自动删除&#xff0c;其中不能存放永久性数据。 用户进程和服务器进程是一对一的叫做专用连接。 任何一个用户连到oracle数据库&#xff0c;oracle都会…

数据结构-基础

1、概念&#xff1a; 程序 数据结构 算法 2、程序的好坏 可读性&#xff0c;稳定性&#xff0c;扩展性&#xff0c;时间复杂度&#xff0c;空间复杂度。 3、数据结构 是指存储、组织数据的方式&#xff0c;以便高效地进行访问和修改。通过选择适当的数据结构&#xff0c; 能…

AGI的基石:什么是机器学习

什么是机器学习&#xff1a;机器学习是人工智能的子集&#xff0c;深度学习是其重要的组成部分&#xff0c;包括不限于&#xff1a;deep neural networks 、deep belief networks和recurrent neural networks。在深度学习中&#xff0c;有三种基本的神经网络架构&#xff1a;FF…

SQL布尔盲注+时间盲注

1.布尔盲注 双重for循环 import requestsurl http://127.0.0.1/sqli-labs-master/Less-8/index.phpdef database_name():datebasename for i in range(1, 9): # 假设数据库名称最多8个字符for j in range(32, 128): # ascii 可见字符范围从32到127payload f"?id1 A…

2024-2025年主流的开源向量数据库推荐

以下是2024-2025年主流的开源向量数据库推荐&#xff0c;涵盖其核心功能和应用场景&#xff1a; 1. Milvus 特点&#xff1a;专为大规模向量搜索设计&#xff0c;支持万亿级向量数据集的毫秒级搜索&#xff0c;适用于图像搜索、聊天机器人、化学结构搜索等场景。采用无状态架…

Mysql中使用sql语句生成雪花算法Id

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

MySQL - 索引 - 介绍

索引(Index)是帮助数据库高效获取数据的数据结构. 结构 语法 创建索引 creat [unique] index 索引名 on 表名 (字段名, ...); //创建唯一索引时加上unique, 多个字段用逗号隔开 查看索引 show index from 表名; 删除索引 drop index 索引名 on 表名;

XSS 常用标签及绕过姿势总结

XSS 常用标签及绕过姿势总结 一、xss 常见标签语句 0x01. 标签 <a href"javascript:alert(1)">test</a> <a href"x" onfocus"alert(xss);" autofocus"">xss</a> <a href"x" onclickeval(&quo…

财务主题数据分析-企业盈利能力分析

企业盈利能力数据主要体现在财务三张表中的利润表里面&#xff0c;盈利能力需要重点需要关注的指标有&#xff1a;毛利率、净利率、净利润增长率、营业成本增长率等&#xff1b; 接下来我们分析一下某上市公司披露的财务数据&#xff0c;看看该企业盈利能力如何&#xff1a; …

你需要了解的远程登录协议——Telnet

你需要了解的远程登录协议——Telnet 一. 什么是Telnet&#xff1f;二. Telnet的优缺点三. Telnet vs SSH&#xff1a;哪一个更适合&#xff1f;四. Telnet的应用场景 前言 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神…

web逆向企鹅音乐,下载歌手歌单音乐

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 下载资源链接&#xff1a;https://download.csdn.net/download/randy521520/90374039 一、找出需要加密的参数 1.js运行 atob…

stm32 lwip tcp服务端频繁接收连接失效问题解决(tcp_recved)

一、问题描述 最近用stmf429单片机作为TCP服务端遇到一个问题&#xff0c;就是客户端特别频繁的发送消息&#xff0c;过一段时间以后&#xff0c;客户端的请求不再被客户端接收到&#xff0c;而且服务器端监控的掉线回调函数也不会被调用&#xff0c;好像这个连接就凭空的消失…

Python Pandas(7):Pandas 数据清洗

数据清洗是对一些没有用的数据进行处理的过程。很多数据集存在数据缺失、数据格式错误、错误数据或重复数据的情况&#xff0c;如果要使数据分析更加准确&#xff0c;就需要对这些没有用的数据进行处理。数据清洗与预处理的常见步骤&#xff1a; 缺失值处理&#xff1a;识别并…