OpenCV学习:从角点检测到特征匹配

`

文章目录

    • 一、角点检测基础
      • [1.1 Harris角点检测原理](#1.1 Harris角点检测原理)
      • [1.2 代码实现与参数解析](#1.2 代码实现与参数解析)
    • 二、SIFT特征提取
      • [2.1 SIFT特征提取流程](#2.1 SIFT特征提取流程)
      • [2.2 关键点属性详解](#2.2 关键点属性详解)
      • [2.3 描述符计算](#2.3 描述符计算)
    • 三、基于特征匹配的指纹验证
      • [3.1 简化版指纹验证系统](#3.1 简化版指纹验证系统)
      • [3.2 匹配参数详解](#3.2 匹配参数详解)
      • [3.3 改进版指纹验证系统](#3.3 改进版指纹验证系统)
    • 四、进阶指纹匹配可视化
      • [4.1 增强的指纹匹配可视化](#4.1 增强的指纹匹配可视化)
      • [4.2 指纹数据识别](#4.2 指纹数据识别)

一、角点检测基础

角点是图像中局部区域与周围区域有较大灰度变化的点,通常位于物体的边缘或纹理丰富的区域。角点检测是许多视觉任务的基础。

1.1 Harris角点检测原理

Harris角点检测算法通过计算图像局部窗口在各个方向移动时灰度值的变化程度来识别角点。当窗口在任意方向移动都会导致灰度值显著变化时,该位置被认为是角点。

1.2 代码实现与参数解析

python 复制代码
import cv2
import numpy as np

# 读取原始图像
img = cv2.imread(r"picture_3.jpg")
# 放大图像以便更好观察
img = cv2.resize(img, None, fx=1.5, fy=1.5)
# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 执行Harris角点检测
dst = cv2.cornerHarris(gray, blockSize=4, ksize=3, k=0.04)

# 标记检测到的角点
img[dst > 0.01 * dst.max()] = [0, 0, 255]

# 显示结果图像
cv2.imshow('img', img)
cv2.waitKey(0)

关键参数解析:

  • blockSize:角点检测中要考虑的邻域大小。这个值决定了检测角点时考虑的局部窗口尺寸,值越大,检测到的角点可能越少但更稳定。

  • ksize:Sobel求导中使用的窗口大小。Sobel算子用于计算图像的梯度,这个参数控制梯度计算的精度。

  • k:Harris角点检测方程中的自由参数,取值范围通常为[0.04, 0.06]。这个值影响角点响应的灵敏度。

角点标记原理:

通过阈值处理dst > 0.01 * dst.max(),我们将角点响应值大于最大响应值1%的像素标记为红色。这种方法可以筛选出最明显的角点。

二、SIFT特征提取

SIFT(尺度不变特征变换)是一种强大的局部特征描述子,具有尺度、旋转和光照不变性。

2.1 SIFT特征提取流程

python 复制代码
# 读取第二张图像
img1 = cv2.imread(r"picture_2.jpg")
# 缩小图像以便处理
img1 = cv2.resize(img1, None, fx=0.5, fy=0.5)
# 转换为灰度图像
img1_gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)

# 创建SIFT特征提取器对象
sift = cv2.SIFT_create()

# 在图像中检测关键点
kp = sift.detect(img1_gray)

# 绘制关键点
img1_sift = cv2.drawKeypoints(img1, kp, outImage=None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 显示结果
cv2.imshow('img1_sift', img1_sift)
cv2.waitKey(0)

# 计算关键点描述符
kp, des = sift.compute(img1, kp)
print(np.array(kp).shape, des.shape)

2.2 关键点属性详解

SIFT检测到的每个关键点包含以下重要属性:

  • kp.pt:关键点的(x, y)坐标,表示该特征点在图像中的位置。

  • kp.size:关键点的大小(尺度),反映了特征点所在的图像金字塔层级。

  • kp.angle:关键点的方向,基于局部图像梯度计算,使特征具有旋转不变性。

  • kp.response:关键点的响应值,表示该特征点的显著程度。

  • kp.octave:关键点所在的金字塔层级,用于实现尺度不变性。

2.3 描述符计算

sift.compute()方法计算关键点的描述符,每个描述符是一个128维的向量,描述了关键点周围区域的局部特征。这些描述符可以用于后续的特征匹配任务。

三、基于特征匹配的指纹验证

特征匹配是将不同图像中的相似特征点关联起来的过程,在指纹识别、人脸识别等生物特征验证中有重要应用。

3.1 简化版指纹验证系统

python 复制代码
import cv2

def cv_show(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)

def verification(src, template):
    # 创建SIFT特征提取器
    sift = cv2.SIFT_create()
    
    # 检测源图像的关键点和计算描述符
    kp1, des1 = sift.detectAndCompute(src, None)
    
    # 检测模板图像的关键点和计算描述符
    kp2, des2 = sift.detectAndCompute(template, None)
    
    # 创建FLANN匹配器
    flann = cv2.FlannBasedMatcher_create()
    
    # 使用k近邻匹配
    matches = flann.knnMatch(des1, des2, k=2)
    
    # 进行比较筛选
    ok = []
    for m, n in matches:
        # 根据Lowe's比率测试,选择最佳匹配
        if m.distance < 0.8 * n.distance:
            ok.append((m, n))
    
    # 统计通过筛选的匹配数量
    num = len(ok)
    
    # 判断验证结果
    if num >= 500:
        result = "认证通过"
    else:
        result = "认证失败"
    
    return result

if __name__ == "__main__":
    # 读取待验证图像和模板图像
    src1 = cv2.imread(r"src1.bmp")
    cv_show("src1", cv2.resize(src1, None, fx=0.5, fy=0.5))
    
    src2 = cv2.imread(r"src2.bmp")
    cv_show("src2", cv2.resize(src2, None, fx=0.5, fy=0.5))
    
    model = cv2.imread(r"model.bmp")
    cv_show("model", cv2.resize(model, None, fx=0.5, fy=0.5))
    
    # 进行验证
    result1 = verification(src1, model)
    result2 = verification(src2, model)
    
    print("src1验证结果为:", result1)
    print("src2验证结果为:", result2)

3.2 匹配参数详解

  • matches:使用k近邻算法进行特征匹配,k=2表示每个特征在模板图像中找2个最近邻。

  • m.distance:匹配的特征点描述符的欧式距离,数值越小说明两个特征点越相近。

  • queryIdx:测试图像的特征点描述符的下标。

  • trainIdx:样本图像的特征点描述符下标。

3.3 改进版指纹验证系统

python 复制代码
import cv2

def cv_show(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)

def verification(src, template):
    # 创建SIFT特征检测器对象
    sift = cv2.SIFT_create()

    # 检测源图像的关键点和计算描述符
    kp1, des1 = sift.detectAndCompute(src, None)

    # 检测模板图像的关键点和计算描述符
    kp2, des2 = sift.detectAndCompute(template, None)

    # 创建FLANN匹配器对象
    flann = cv2.FlannBasedMatcher_create()

    # 使用k近邻算法进行特征匹配
    matches = flann.knnMatch(des1, des2, k=2)

    # 初始化存储匹配点坐标的列表
    matched_points_src = []
    matched_points_template = []

    # 遍历所有匹配对
    for m, n in matches:
        # Lowe's比率测试: 筛选最佳匹配
        if m.distance < 0.4 * n.distance:
            # 获取源图像匹配点的坐标
            src_pt = kp1[m.queryIdx].pt
            
            # 获取模板图像匹配点的坐标
            template_pt = kp2[m.trainIdx].pt
            
            # 将浮点坐标转换为整数并添加到列表
            matched_points_src.append((int(src_pt[0]), int(src_pt[1])))
            matched_points_template.append((int(template_pt[0]), int(template_pt[1])))

    # 计算成功匹配点的数量
    num = len(matched_points_src)

    # 创建图像的副本用于绘制标记点
    src_with_points = src.copy()
    template_with_points = template.copy()

    # 在源图像上绘制匹配点标记
    for pt in matched_points_src:
        cv2.circle(src_with_points, pt, 3, (0, 0, 255), -1)

    # 在模板图像上绘制匹配点标记
    for pt in matched_points_template:
        cv2.circle(template_with_points, pt, 3, (0, 0, 255), -1)

    # 根据匹配点数量判断验证结果
    result = "认证通过" if num >= 10 else "认证失败"

    # 返回验证结果和标记后的图像
    return result, src_with_points, template_with_points, matched_points_src, matched_points_template

if __name__ == "__main__":
    # 读取图像文件
    src1 = cv2.imread(r"src1.bmp")
    src2 = cv2.imread(r"src2.bmp")
    model = cv2.imread(r"model.bmp")

    # 对第一个图像进行验证
    result1, src1_points, model1_points, src1_coords, model1_coords = verification(src1, model)
    print("src1验证结果为:", result1)

    # 对第二个图像进行验证
    result2, src2_points, model2_points, src2_coords, model2_coords = verification(src2, model)
    print("src2验证结果为:", result2)

    # 显示匹配结果
    cv_show("src1", cv2.resize(src1_points, None, fx=0.5, fy=0.5))
    cv_show("model1", cv2.resize(model1_points, None, fx=0.5, fy=0.5))
    cv_show("src2", cv2.resize(src2_points, None, fx=0.5, fy=0.5))
    cv_show("model2", cv2.resize(model2_points, None, fx=0.5, fy=0.5))


四、进阶指纹匹配可视化

在上一部分我们介绍了基本的特征匹配技术后,现在进一步探讨更实用的指纹识别应用场景。以下是两个更完善的指纹识别系统,分别专注于匹配结果的可视化和基于数据库的指纹身份识别。

4.1 增强的指纹匹配可视化

python 复制代码
import cv2

def cv_show(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)

# 读取图像
src1 = cv2.imread("D:\Code\PythonTest\src1.bmp")
cv_show('src1', src1)
model = cv2.imread("D:\Code\PythonTest\model.bmp")
cv_show('model', model)

# 创建SIFT特征提取器
sift = cv2.SIFT_create()
# 检测关键点和计算描述符
kp1, des1 = sift.detectAndCompute(src1, None)
kp2, des2 = sift.detectAndCompute(model, None)

# 创建FLANN匹配器
flann = cv2.FlannBasedMatcher_create()
matches = flann.knnMatch(des1, des2, k=2)

# 筛选匹配点
good = []
alist = []
for m, n in matches:
    if m.distance < 0.4 * n.distance:  # Lowe's比率测试
        alist.append((m.queryIdx, m.trainIdx))  # 匹配成功的(指纹1中的索引,指纹model中的索引)
        good.append((m, n))

# 在图像上标记匹配点
for i, j in alist:
    x, y = kp1[i].pt
    m, n = kp2[j].pt
    cv2.circle(src1, center=(int(x), int(y)), radius=3, color=(0, 0, 255), thickness=-1)
    cv2.circle(model, center=(int(m), int(n)), radius=3, color=(0, 0, 255), thickness=-1)

# 显示标记后的图像
cv_show('Marked src1', src1)
cv_show('Marked model', model)

# 绘制匹配点连线
draw_img = cv2.drawMatchesKnn(src1, kp1, model, kp2, good, None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
cv_show('Matches', draw_img)
  • 解析:

1. 匹配点索引存储

python 复制代码
alist.append((m.queryIdx, m.trainIdx))

这里不仅筛选出好的匹配点,还保存了匹配点对在两个图像中的索引。queryIdx是源图像中关键点的索引,trainIdx是模板图像中关键点的索引。

2. 坐标提取与标记

python 复制代码
x, y = kp1[i].pt
m, n = kp2[j].pt

通过索引从关键点列表中提取实际坐标,然后在两个图像上分别用红色实心圆标记出来,便于直观观察匹配点的分布。

3. 可视化匹配关系

python 复制代码
draw_img = cv2.drawMatchesKnn(src1, kp1, model, kp2, good, None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)

cv2.drawMatchesKnn函数创建一个新图像,将两个输入图像并排显示,并用线条连接匹配的关键点。flags参数设置为NOT_DRAW_SINGLE_POINTS表示不单独绘制未匹配的关键点,只显示匹配对。

4.2 指纹数据识别

接下来是一个更复杂的系统,实现了从指纹数据库中进行身份识别的功能:

python 复制代码
import os
import cv2

# 计算两个指纹间匹配点的个数
def getNum(src, model):
    img1 = cv2.imread(src)
    img2 = cv2.imread(model)
    sift = cv2.SIFT_create()  # orb_create()
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)
    flann = cv2.FlannBasedMatcher_create()
    matches = flann.knnMatch(des1, des2, k=2)
    ok = []
    for m, n in matches:
        if m.distance < 0.8 * n.distance:
            ok.append(m)
    num = len(ok)
    return num

# 获取指纹编号
def getID(src, database):
    max = 0
    name = ""
    for file in os.listdir(database):
        model = os.path.join(database, file)
        num = getNum(src, model)
        print("文件名:", file, "匹配点个数:", num)
        if num > max:
            max = num
            name = file
    if max < 200:  # src图片不一定是库里面人的指纹
        ID = 9999
    else:
        # 假设文件名格式如 "0_finger.bmp",取第一个字符作为ID
        ID = name.split('_')[0] if '_' in name else name[0]
    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"
    ID = getID(src, database)
    name = getName(ID)
    print("识别结果为:", name)
  • 解析:

1. 数据搜索

python 复制代码
for file in os.listdir(database):
    model = os.path.join(database, file)
    num = getNum(src, model)

系统遍历数据库中的所有指纹模板,逐一与待识别指纹进行匹配。这种暴力搜索方法适用于小型数据库,对于大型数据库可能需要更高效的索引方法。

2. 匹配阈值

python 复制代码
if max < 200:  # src图片不一定是库里面人的指纹
    ID = 9999

设置了200个匹配点的阈值,只有最大匹配点数量超过这个值才认为识别成功。这个阈值需要根据具体应用场景进行调整。

3. ID映射

python 复制代码
nameID = {0: '张三', 1: '李四', 2: '王五', ...}

使用字典将数字ID映射到实际姓名,便于扩展和维护。当需要添加新用户时,只需在字典中添加新的键值对。

相关推荐
行业探路者2 小时前
音频二维码让音频分享变得更简单快捷
学习·音视频·语音识别·二维码·设备巡检
●VON2 小时前
无状态 Widget 下的实时排序:Flutter for OpenHarmony 中 TodoList 的排序策略与数据流控制
学习·flutter·架构·交互·openharmony·von
●VON2 小时前
面向 OpenHarmony 的 Flutter 应用实战:TodoList 多条件过滤系统的状态管理与性能优化
学习·flutter·架构·跨平台·von
LN花开富贵2 小时前
LM393的工作原理和引脚作用
笔记·单片机·嵌入式硬件·学习·嵌入式
●VON2 小时前
Flutter for OpenHarmony:基于不可变更新与局部状态隔离的 TodoList 任务编辑子系统实现
学习·flutter·openharmony·布局·技术·von
xiaobuding_QAQ2 小时前
51汇编仿真proteus8.15学习篇四(附源码)
汇编·单片机·学习·proteus
解局易否结局2 小时前
学习 Flutter for OpenHarmony 的前置 Dart 语言:高级特性实战笔记(下)
笔记·学习·flutter
峥嵘life2 小时前
Android16 EDLA【GTS】GtsUnofficialApisUsageTestCases存在fail项
android·linux·运维·学习
我的xiaodoujiao2 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 44--Pytest框架钩子函数
python·学习·测试工具·pytest