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

计算机视觉----opencv实战----指纹识别的案例

一、数据准备

src2.BMP

src1.BMP

src.bmp

model.BMP

二、识别原理讲解(sift特征提取)

SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)是一种经典的图像特征提取算法,核心优势是不受图像尺度缩放、旋转、光照变化的影响,能稳定提取图像中的关键特征点,广泛用于图像匹配、目标检测、图像拼接等场景。以下从核心原理、OpenCV 实现步骤、关键特性三方面展开介绍,全程不涉及公式。

核心原理(4 个关键步骤)

SIFT 的本质是通过 “模拟人眼对不同尺度物体的感知”,找到图像中 “无论放大 / 缩小、旋转都不变” 的特征点,并为每个特征点生成唯一的 “特征描述符”(用于后续匹配)。整个过程可拆解为 4 步:

1. 尺度空间极值检测:找 “不受尺度影响” 的候选特征点

人眼观察物体时,近距离看细节、远距离看整体 ——SIFT 通过 “高斯模糊 + 图像缩放” 构建 “尺度空间”,模拟这种感知过程:

  • 对原始图像做不同程度的高斯模糊(模糊程度逐渐增加),再对模糊后的图像做下采样(缩小尺寸),得到一系列 “不同尺度” 的图像集合(称为 “高斯金字塔”)。
  • 在相邻尺度的图像间做差值(得到 “差分高斯金字塔”),然后在每个像素点的 “上下左右相邻像素 + 相邻尺度对应位置像素” 中比较,找到 “局部极值点”—— 这些点就是 “在不同尺度下都突出” 的候选特征点(比如小尺度下的角点、大尺度下的轮廓顶点)。
2. 特征点精确定位:剔除 “不稳定” 的候选点

第一步找到的候选点中,可能包含因噪声、边缘干扰产生的 “假特征点”,需要进一步筛选:

  • 对每个候选极值点,分析其周围像素的灰度变化,判断该点是否是 “真正的特征点”(比如边缘上的点会被剔除,因为边缘在垂直方向的灰度变化不显著,稳定性差)。
  • 最终保留 “灰度变化显著、在尺度上稳定” 的点,作为最终的 SIFT 特征点。
3. 特征点方向赋值:实现 “旋转不变性”

为了让特征点不受图像旋转影响,需要给每个特征点分配一个 “主方向”:

  • 以特征点为中心,取一个小区域(比如半径 16 像素的圆),统计该区域内所有像素的 “梯度方向”(即像素灰度变化的方向,比如从暗到亮的方向)和 “梯度大小”(灰度变化的强度)。
  • 用 “直方图” 统计这些梯度方向的分布,找到出现次数最多的方向(主方向),将该方向作为特征点的 “基准方向”—— 后续生成描述符时,会以这个主方向为参考,从而抵消旋转的影响。
4. 生成特征描述符:让特征 “可匹配”

每个特征点需要一个 “唯一标识”(描述符),用于和其他图像中的特征点对比匹配:

  • 以特征点为中心,取一个 16×16 的像素块(按主方向对齐,避免旋转干扰),将这个块分成 4×4 的 16 个小格子(每个小格子 4×4 像素)。
  • 对每个小格子,统计其中像素的梯度方向分布(用 8 个方向的直方图表示),得到 8 个数值。
  • 16 个小格子共生成 16×8=128 个数值,将这 128 个数值组成一个向量,就是该特征点的 “128 维 SIFT 描述符”。
  • 最后会对描述符做 “归一化” 处理(比如消除光照变化的影响:让描述符向量的长度为 1),确保其在不同光照下仍能稳定匹配

三、对比检测指纹(简单)

这里我们需要用到(src1.BMP,src2.BMP,model.BMP)三张图片,在(src1.BMP,src2.BMP)找出和model.BMP匹配的图片

代码示例:

1. 导入依赖库
import cv2

导入 OpenCV 库,它提供了强大的计算机视觉处理功能,包括 SIFT 特征提取和 FLANN 匹配器。

2. 核心认证函数 verification

该函数接收三个参数:

  • src:待验证的源图像
  • model:作为标准的模型图像
  • threshold:判断认证通过的匹配点数量阈值,默认值为 500

函数执行流程:

(1)初始化 SIFT 特征提取器
sift = cv2.SIFT_create()

SIFT(尺度不变特征变换)是一种对尺度、旋转、光照变化都具有稳健性的特征提取算法,非常适合用于图像匹配。

(2)提取图像特征点和描述符
kp1, des1 = sift.detectAndCompute(src, None)
kp2, des2 = sift.detectAndCompute(model, None)
  • kp1/kp2:分别是源图像和模型图像的特征点(KeyPoint)集合
  • des1/des2:分别是对应特征点的描述符(Descriptor),是特征点的数字表示
(3)特征点检查
if des1 is None or des2 is None:return "认证失败"  # 无特征点

如果任何一幅图像无法提取到特征点,直接返回认证失败。

(4)FLANN 特征匹配
flann = cv2.FlannBasedMatcher()
matches = flann.knnMatch(des1, des2, k=2)
  • 使用 FLANN(快速最近邻搜索库)匹配器进行特征匹配,比暴力匹配更高效
  • knnMatch 方法返回每个特征点的前 k 个最近邻匹配(这里 k=2)
(5)筛选优质匹配
good_matches = [m for m, n in matches if m.distance < 0.8 * n.distance]

应用 Lowe's 比率测试筛选优质匹配:如果最佳匹配距离小于次佳匹配距离的 80%,则认为是一个好的匹配点,这能有效剔除误匹配。

(6)返回认证结果
return "认证通过" if len(good_matches) >= threshold else "认证失败"

如果优质匹配点数量达到或超过阈值,则认证通过,否则失败。

3. 主程序

if __name__ == "__main__":src1 = cv2.imread("src1.BMP")src2 = cv2.imread("src2.BMP")model = cv2.imread("model.BMP")

读取待验证图像(src1.BMP、src2.BMP)和模型图像(model.BMP)。

if src1 is None or src2 is None or model is None:print("❌ 图像读取失败,请检查文件路径")

检查图像是否成功读取,如果有任何图像读取失败,提示检查文件路径。

else:print("src1验证结果:", verification(src1, model))print("src2验证结果:", verification(src2, model))

如果所有图像都成功读取,则分别对 src1 和 src2 进行认证,并打印结果。

完整代码:

import cv2def verification(src, model, threshold=500):"""使用SIFT + FLANN匹配,返回认证结果"""sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(src, None)kp2, des2 = sift.detectAndCompute(model, None)if des1 is None or des2 is None:return "认证失败"  # 无特征点flann = cv2.FlannBasedMatcher()matches = flann.knnMatch(des1, des2, k=2)good_matches = [m for m, n in matches if m.distance < 0.8 * n.distance]return "认证通过" if len(good_matches) >= threshold else "认证失败"# ========== 主程序 ==========
if __name__ == "__main__":src1 = cv2.imread("src1.BMP")src2 = cv2.imread("src2.BMP")model = cv2.imread("model.BMP")if src1 is None or src2 is None or model is None:print("❌ 图像读取失败,请检查文件路径")else:print("src1验证结果:", verification(src1, model))print("src2验证结果:", verification(src2, model))

运行结果:

四、进阶任务(多图片匹配)

现在需要对(src.bmp)在指纹库里进行匹配并绘制出匹配上的点(指纹库:database,已经上传,可以自行下载)

代码详解:

1. 导入依赖库
import os          # 用于文件和目录操作
import cv2         # OpenCV库,用于图像处理和特征提取
import numpy as np # 用于数值计算和数组操作
2. 核心函数:获取匹配点
def get_good_matches(src, model):# 读取源图像和模板图像img1 = cv2.imread(src)img2 = cv2.imread(model)if img1 is None or img2 is None:return [], [], []# 创建SIFT特征提取器并计算特征点和描述符sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(img1, None)  # kp: 关键点, des: 描述符kp2, des2 = sift.detectAndCompute(img2, None)if des1 is None or des2 is None:return kp1 or [], kp2 or [], []# 使用FLANN匹配器进行特征匹配flann = cv2.FlannBasedMatcher()matches = flann.knnMatch(des1, des2, k=2)  # k=2表示返回两个最佳匹配# 应用Lowe's比例测试筛选良好的匹配点good = []for m, n in matches:# 如果最佳匹配距离小于次佳匹配的80%,则认为是好的匹配if m.distance < 0.8 * n.distance:good.append(m)return kp1, kp2, good
3. 计算匹配点个数
def getNum(src, model):_, _, good = get_good_matches(src, model)return len(good)

这个函数简化了匹配点获取过程,只返回良好匹配点的数量,用于比较不同模板的匹配程度。

4. 获取指纹编号
def getID(src, database):max_num = 0best_name = "0.bmp"  # 默认值,防止未匹配时报错# 遍历数据库中的所有文件for file in os.listdir(database):model = os.path.join(database, file)num = getNum(src, model)print(f"文件名:{file},匹配点个数:{num}")# 记录匹配点最多的文件if num > max_num:max_num = numbest_name = file# 如果匹配点数量大于等于100,则认为匹配有效ID = int(best_name[0]) if max_num >= 100 else 9999return ID

该函数通过比较输入图像与数据库中所有图像的匹配点数量,找到最相似的图像,并返回其对应的 ID。

5. 根据 ID 获取姓名
def getName(ID):nameID = {0: '张三', 1: '李四', 2: '王五', 3: '赵六', 4: '朱老七',5: '钱八', 6: '曹九', 7: '王二麻子', 8: 'andy', 9: 'Anna',9999: "没找到"}return nameID.get(int(ID), "未知")

这是一个简单的 ID 与姓名映射表,根据识别出的 ID 返回对应的姓名。

6. 绘制匹配点
def drawMatchesWithCircles(src, model, show=True):img1 = cv2.imread(src)img2 = cv2.imread(model)kp1, kp2, good_matches = get_good_matches(src, model)# 在两张图像上绘制匹配点(红色实心圆)for match in good_matches:pt1 = tuple(map(int, kp1[match.queryIdx].pt))  # 源图像上的匹配点pt2 = tuple(map(int, kp2[match.trainIdx].pt))  # 模板图像上的匹配点cv2.circle(img1, pt1, 3, (0, 0, 255), -1)  # -1表示填充圆cv2.circle(img2, pt2, 3, (0, 0, 255), -1)# 拼接两张图像以便对比显示h1, w1 = img1.shape[:2]h2, w2 = img2.shape[:2]h = max(h1, h2)  # 取最大高度w = w1 + w2      # 宽度相加combined = np.zeros((h, w, 3), dtype=np.uint8)combined[:h1, :w1] = img1combined[:h2, w1:w1+w2] = img2# 显示结果if show:cv2.imshow("Matches with Circles", combined)cv2.waitKey(0)cv2.destroyAllWindows()return len(good_matches)
7. 主程序
if __name__ == "__main__":src = "src.bmp"       # 待识别的源图像database = "database" # 模板数据库目录# 执行识别流程ID = getID(src, database)name = getName(ID)print("识别结果是:", name)# 绘制最佳匹配的匹配点if ID != 9999:# 找到最佳匹配的模板文件best_model = os.path.join(database, [f for f in os.listdir(database) if f.startswith(str(ID))][0])print(f"\n正在绘制 {best_model} 与 {src} 的匹配点...")drawMatchesWithCircles(src, best_model)else:print("未找到有效匹配,不进行绘制。")
工作流程总结
  1. 读取待识别图像 (src.bmp) 和数据库中的所有模板图像
  2. 对每对图像使用 SIFT 算法提取特征点并进行匹配
  3. 统计匹配点数量,找到匹配度最高的模板
  4. 根据模板的 ID 查找对应的姓名并输出
  5. 可视化显示最佳匹配的特征点对应关系
完整代码:
import os
import cv2
import numpy as np############## 获取匹配点(核心函数) #####################
def get_good_matches(src, model):img1 = cv2.imread(src)img2 = cv2.imread(model)if img1 is None or img2 is None:return [], [], []sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(img1, None)kp2, des2 = sift.detectAndCompute(img2, None)if des1 is None or des2 is None:return kp1 or [], kp2 or [], []flann = cv2.FlannBasedMatcher()matches = flann.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.8 * n.distance:good.append(m)return kp1, kp2, good############## 计算匹配个数 #####################
def getNum(src, model):_, _, good = get_good_matches(src, model)return len(good)############# 获取指纹编号 ################
def getID(src, database):max_num = 0best_name = "0.bmp"  # 默认值,防止未匹配时报错for file in os.listdir(database):model = os.path.join(database, file)num = getNum(src, model)print(f"文件名:{file},匹配点个数:{num}")if num > max_num:max_num = numbest_name = fileID = int(best_name[0]) if max_num >= 100 else 9999return ID############# 获取姓名 ################
def getName(ID):nameID = {0: '张三', 1: '李四', 2: '王五', 3: '赵六', 4: '朱老七',5: '钱八', 6: '曹九', 7: '王二麻子', 8: 'andy', 9: 'Anna',9999: "没找到"}return nameID.get(int(ID), "未知")############## 绘制匹配点(实心小圆圈) #####################
def drawMatchesWithCircles(src, model, show=True):img1 = cv2.imread(src)img2 = cv2.imread(model)kp1, kp2, good_matches = get_good_matches(src, model)# 绘制匹配点for match in good_matches:pt1 = tuple(map(int, kp1[match.queryIdx].pt))pt2 = tuple(map(int, kp2[match.trainIdx].pt))cv2.circle(img1, pt1, 3, (0, 0, 255), -1)cv2.circle(img2, pt2, 3, (0, 0, 255), -1)# 拼接显示h1, w1 = img1.shape[:2]h2, w2 = img2.shape[:2]h = max(h1, h2)w = w1 + w2combined = np.zeros((h, w, 3), dtype=np.uint8)combined[:h1, :w1] = img1combined[:h2, w1:w1+w2] = img2if show:cv2.imshow("Matches with Circles", combined)cv2.waitKey(0)cv2.destroyAllWindows()return len(good_matches)############# 主程序 ################
if __name__ == "__main__":src = "src.bmp"database = "database"ID = getID(src, database)name = getName(ID)print("识别结果是:", name)# 绘制最佳匹配if ID != 9999:best_model = os.path.join(database, [f for f in os.listdir(database) if f.startswith(str(ID))][0])print(f"\n正在绘制 {best_model} 与 {src} 的匹配点...")drawMatchesWithCircles(src, best_model)else:print("未找到有效匹配,不进行绘制。")详细介绍代码
运行结果:


文章转载自:

http://qUyWWjYf.dmkhd.cn
http://rRj0O7xg.dmkhd.cn
http://TCUc1y54.dmkhd.cn
http://X8cN36ys.dmkhd.cn
http://7eVsYv03.dmkhd.cn
http://2RED5gtX.dmkhd.cn
http://Cxgfdg0l.dmkhd.cn
http://w3Vu3b7b.dmkhd.cn
http://wopdw91W.dmkhd.cn
http://KqwqWHtl.dmkhd.cn
http://QMcAa7cw.dmkhd.cn
http://QZmJelSo.dmkhd.cn
http://qEFbCqpN.dmkhd.cn
http://SgOlamVo.dmkhd.cn
http://m4tafUNj.dmkhd.cn
http://9XYsHu8E.dmkhd.cn
http://b8u4iot7.dmkhd.cn
http://8cW7W0i2.dmkhd.cn
http://gaD9K5EU.dmkhd.cn
http://thQI65Gk.dmkhd.cn
http://qbeQwrzD.dmkhd.cn
http://RyVPszdl.dmkhd.cn
http://QEwZcGGB.dmkhd.cn
http://09mJS4m0.dmkhd.cn
http://pUqWTkgK.dmkhd.cn
http://TQb7iNqV.dmkhd.cn
http://GgOQew9P.dmkhd.cn
http://3wA1FJob.dmkhd.cn
http://aVYjUspg.dmkhd.cn
http://KDpXwCF7.dmkhd.cn
http://www.dtcms.com/a/381548.html

相关文章:

  • 【操作系统核心知识梳理】线程(Thread)重点与易错点全面总结
  • JVM之堆(Heap)
  • 【网络编程】TCP 服务器并发编程:多进程、线程池与守护进程实践
  • 智能体赋能金融多模态报告自动化生成:技术原理与实现流程全解析
  • 数据库(一)数据库基础及MySql 5.7+的编译安装
  • 将 x 减到 0 的最小操作数
  • Java 开发工具,最新2025 IDEA使用(附详细教程)
  • 基于STM32单片机的OneNet物联网粉尘烟雾检测系统
  • 注意力机制与常见变种-概述
  • Linux内核TCP协议实现深度解析
  • 数据治理进阶——40页数据治理的基本概念【附全文阅读】
  • Spring Boot 与前端文件下载问题:大文件、断点续传与安全校验
  • 认知语义学中的象似性对人工智能自然语言处理深层语义分析的影响与启示
  • 游戏服务器使用actor模型
  • 002 Rust环境搭建
  • 2.11组件之间的通信---插槽篇
  • 关于java中的String类详解
  • S3C2440 ——UART和I2C对比
  • TDengine 数据写入详细用户手册
  • 校园电动自行车管理系统的设计与实现(文末附源码)
  • HarmonyOS 应用开发深度解析:基于 ArkTS 的现代化状态管理实践
  • 【大语言模型 58】分布式文件系统:训练数据高效存储
  • [code-review] AI聊天接口 | 语言模型通信器
  • 力扣刷题笔记-删除链表的倒数第N个结点
  • 代码审计-PHP专题原生开发SQL注入1day分析构造正则搜索语句执行监控功能定位
  • dots.llm1:小红书开源的 MoE 架构大语言模型
  • --gpu-architecture <arch> (-arch)
  • uniapp动态修改tabbar
  • Spring Boot 集成 Flowable 7.1.0 完整教程
  • 教你使用服务器如何搭建数据库