计算机视觉——Opencv(指纹认证和指纹识别)
在计算机视觉领域,是实现图像认证、目标识别、指纹 / 人脸验证等功能的核心技术,而 SIFT 结合 FLANN 的匹配方案,因具备尺度不变、旋转不变的鲁棒性和高效的匹配速度,成为此类项目的经典选择。
在计算机视觉领域,特征匹配是实现图像认证、目标识别、指纹 / 人脸验证等功能的核心技术,而 SIFT 结合 FLANN 的匹配方案,因具备尺度不变、旋转不变的鲁棒性和高效的匹配速度,成为此类项目的经典选择
一、核心逻辑
提取待认证图像与模板图像的 SIFT 特征点和描述符,通过 FLANN 算法进行 K 近邻特征匹配,再利用 Lowe 比率测试筛选有效匹配点,最终根据有效匹配点的数量是否达到阈值(500),判断图像认证是否通过,可同时对多张待认证图像进行批量验证。
本文使用 SIFT 特征点检测 和 FLANN 匹配器 实现简单的指纹认证和指纹识别
二、核心概念
1. SIFT 特征提取
SIFT(尺度不变特征变换)能在图像中检测出关键点(如角点、边缘点,不受图像缩放、旋转、轻微光照变化影响),并为每个关键点计算128 维特征描述符(用向量描述关键点周围像素分布,向量越相似,特征点匹配度越高)。
2. FLANN 特征匹配
FLANN(快速近似最近邻库)是高效的特征匹配算法,相比暴力匹配,在特征点数量较多时匹配速度更快,适合实际场景的高效匹配需求,代码中用其实现 K 近邻匹配(K=2)。
3. Lowe 比率测试
用于剔除特征匹配中的虚假匹配(如相似纹理导致的错误匹配):对每个待匹配特征点,取其在模板图像中最近邻和次近邻的匹配距离,若最近邻距离 < 0.8× 次近邻距离,则判定为有效匹配点,否则剔除。
三、指纹认证
1.基础指纹认证
import cv2
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
def verification(src, model):
# 创建SIFT特征提取器
sift = cv2.SIFT_create()
# 检测关键点和计算描述符(特征向量) 源图像
kp1, des1 = sift.detectAndCompute(src, None) # 第二个参数:掩膜
# 检测关键点和计算描述符 模板图像
kp2, des2 = sift.detectAndCompute(model, None)
# 创建FLANN匹配器
flann = cv2.FlannBasedMatcher()
# 使用k近邻匹配(des1中的每个描述符与des2中的最近两个描述符进行匹配)
matches = flann.knnMatch(des1, des2, k=2)
# distance:匹配的特征点描述符的欧式距离,数值越小也就说明两个特征点越相近。
# queryIdx:测试图像的特征点描述符的下标(第几个特征点描述符),同时也是描述符对应特征点的下标。
# trainIdx:样本图像的特征点描述符下标,同时也是描述符对应特征点的下标。
# 进行比较筛选
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"C:\Users\LEGION\Desktop\2a72c3384c9818424f5b46dea6bdb835.bmp")
cv_show(name='src1', img=src1)
src2 = cv2.imread(r"C:\Users\LEGION\Desktop\7621f27ada4aa4f3f3c3027587f01f2a.bmp")
cv_show(name='src2', img=src2)
model = cv2.imread(r"C:\Users\LEGION\Desktop\5cdb73f36e8f3d1d256bb6a5d9dc19a7.bmp")
cv_show(name='model', img=model)
result1 = verification(src1, model)
result2 = verification(src2, model)
print("src1验证结果为:", result1)
print("src2验证结果为:", result2)
代码注释:
sift.detectAndCompute(src, None)
-
detectAndCompute:一站式实现关键点检测和特征描述符计算; -
参数
None:表示掩膜,即对整幅图像进行特征提取,若设置掩膜则仅提取掩膜区域的特征; -
返回值:
kp1/kp2为图像的关键点集合,des1/des2为对应的 128 维特征描述符数组
matches = flann.knnMatch(des1, des2, k=2)
-
创建无参数配置的 FLANN 匹配器,直接调用
knnMatch实现 K 近邻匹配; -
k=2:为每个待认证图像的特征描述符,匹配模板图像中最相似的 2 个描述符,为后续 Lowe 比率测试做准备; -
返回值
matches:所有匹配结果的集合,每个元素包含最近邻和次近邻两个匹配结果
m.distance/n.distance:匹配结果的欧式距离,数值越小表示特征点越相似;
筛选规则:仅保留最近邻距离小于 0.8 倍次近邻距离的匹配结果,存入ok列表,作为有效匹配点。
运行结果:


2.匹配点可视化的指纹认证
相比基础的图像认证代码,本次实现的系统新增 / 优化了五大实用功能,兼顾实用性、鲁棒性和直观性:
-
匹配点可视化:在待验证图像和模板图像上用红色实心圆标注所有有效匹配点,直观查看匹配位置;
-
匹配信息输出:控制台打印每个匹配点的坐标(待验证图 + 模板图)和匹配距离,便于分析匹配质量;
-
鲁棒性提升:增加特征描述符判空、图像读取失败检查,避免程序因异常场景崩溃;
-
匹配效率优化:配置 FLANN 官方推荐的 KD 树参数,平衡匹配速度与精准度;
-
阈值合理化:结合实际匹配场景降低有效匹配阈值和 Lowe 比率阈值,解决原阈值过高导致的匹配失败问题。
代码如下:
import cv2
import numpy as np
def cv_show(name, img):
"""
显示图像,窗口持续保留(不自动关闭)
参数:
name: 窗口名称(需唯一)
img: 要显示的图像
"""
cv2.imshow(name, img) # 仅显示图像,不调用waitKey和destroyWindow
def verification(src, model):
"""
特征匹配并标注匹配点
参数:
src: 待验证图像
model: 模板图像
返回:
result: 验证结果(认证通过/失败)
marked_src: 标注了匹配点的待验证图像
marked_model: 标注了匹配点的模板图像
"""
# 创建SIFT特征提取器
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 "认证失败", src.copy(), model.copy()
# 创建FLANN匹配器(优化匹配参数)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50) # 搜索次数,越高越准确但越慢
flann = cv2.FlannBasedMatcher(index_params, search_params)
# k近邻匹配(k=2:每个特征点找最近的2个匹配)
matches = flann.knnMatch(des1, des2, k=2)
# 筛选满足条件的匹配点(distance < 0.4 * 次近distance)
good_matches = []
for m, n in matches:
if m.distance < 0.4 * n.distance:
good_matches.append(m)
# 统计匹配数量,判断验证结果
num = len(good_matches)
if num >= 50: # 降低阈值(原500过高,实际匹配很难达到)
result = "认证通过"
else:
result = "认证失败"
# 复制图像用于标注(避免修改原图像)
marked_src = src.copy()
marked_model = model.copy()
# 标注匹配点(红色实心圆,半径3)
for match in good_matches:
# 获取待验证图像的匹配点坐标
src_idx = match.queryIdx
src_point = kp1[src_idx].pt # (x, y) 浮点型坐标
x1, y1 = int(src_point[0]), int(src_point[1])
# 绘制待验证图像的匹配点
cv2.circle(marked_src, (x1, y1), 3, (0, 0, 255), -1)
# 获取模板图像的匹配点坐标
model_idx = match.trainIdx
model_point = kp2[model_idx].pt
x2, y2 = int(model_point[0]), int(model_point[1])
# 绘制模板图像的匹配点
cv2.circle(marked_model, (x2, y2), 3, (0, 0, 255), -1)
# 打印匹配点坐标(可选)
print(f"匹配点 - 待验证图: ({x1}, {y1}) | 模板图: ({x2}, {y2}) | 距离: {m.distance:.2f}")
return result, marked_src, marked_model
if __name__ == "__main__":
# 读取图像(确保路径正确)
src1 = cv2.imread(r"C:\Users\LEGION\Desktop\2a72c3384c9818424f5b46dea6bdb835.bmp")
src2 = cv2.imread(r"C:\Users\LEGION\Desktop\7621f27ada4aa4f3f3c3027587f01f2a.bmp")
model = cv2.imread(r"C:\Users\LEGION\Desktop\5cdb73f36e8f3d1d256bb6a5d9dc19a7.bmp")
# 检查图像是否读取成功
if src1 is None or src2 is None or model is None:
print("错误:图像文件读取失败,请检查文件路径!")
else:
# 验证src1并标注匹配点
result1, marked_src1, marked_model1 = verification(src1, model)
# 验证src2并标注匹配点
result2, marked_src2, marked_model2 = verification(src2, model)
# 显示所有结果图像(所有窗口同时保留)
print("src1验证结果为:", result1)
cv_show(name='src1_matched', img=marked_src1)
cv_show(name='model_matched_with_src1', img=marked_model1)
print("src2验证结果为:", result2)
cv_show(name='src2_matched', img=marked_src2)
cv_show(name='model_matched_with_src2', img=marked_model2)
# 等待用户按任意键,然后关闭所有窗口
print("\n所有图像已显示,按任意键关闭所有窗口...")
cv2.waitKey(0) # 阻塞等待按键
cv2.destroyAllWindows() # 关闭所有打开的窗口
运行结果:

-
弹出 4 个窗口:src1 标注图、src1 对应的模板标注图、src2 标注图、src2 对应的模板标注图;
-
窗口中红色实心圆为有效匹配点,匹配点越密集,说明图像相似度越高。
3.匹配点连线的指纹认证
import cv2
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
# 读取图像
src1 = cv2.imread(r"C:\Users\LEGION\Desktop\2a72c3384c9818424f5b46dea6bdb835.bmp")
cv_show("src1", src1)
model = cv2.imread(r"C:\Users\LEGION\Desktop\5cdb73f36e8f3d1d256bb6a5d9dc19a7.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()
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)) # 匹配成功的(指src1中的索引,指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)
# 绘制匹配点连线
# drawMatchesKnn(img1, keypoints1, img2, keypoints2, matches1to2, outImg, matchColor=None, singlePointColor=None, matchesMask=None, flags=None)
# 参数:
# img1: 第一张原始图像的关键点。
# img2: 第二张原始图像。
# keypoints1: 第一张原始图像的关键点。
# keypoints2: 第二张原始图像的关键点。
# matches1to2: 从第一个图像到第二个图像的匹配,这意味着keypoints1[i]在keypoints2[Matches[i]]中有一个对应的点。
# outImg: 绘制结果图像。
# matchColor: 匹配连线与关键点的颜色,当matchColor == Scalar::all(-1)时,代表随机颜色。
# singlePointColor: 没有匹配项的关键点的颜色,当singlePointColor == Scalar::all(-1)时,代表随机颜色。
# matchesMask: 确定绘制哪些匹配项的掩码,如果掩码为空,则绘制所有匹配项。
# flags: 绘制功能的一些标志,具体有:
# cv.DRAW_MATCHES_FLAGS_DEFAULT
# cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
# cv.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG
# cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS
matched_image = cv2.drawMatchesKnn(src1, kp1, model, kp2, good, outImg=None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
cv_show("Matched Points", matched_image)
cv2.drawMatchesKnn:专门用于 k 近邻匹配结果的连线绘制,核心参数说明:
-
src1, kp1:待验证图及其关键点; -
model, kp2:模板图及其关键点; -
good:筛选后的有效匹配结果; -
outImg=None:自动创建结果图像; -
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:屏蔽无匹配的单点,只显示有对应匹配的点和连线,让结果更清晰;
运行结果:

四、指纹识别
流程如下:
-
计算待验证指纹与库中单个指纹的有效匹配点数量;
-
遍历本地指纹库,找到匹配点数量最多的指纹文件;
-
从文件名提取人员编号,通过字典映射为姓名;
-
设定匹配点阈值(200),若最大匹配数低于阈值,判定为 “库中无匹配指纹”
import os
import cv2
"""===============计算两个指纹间匹配点的个数==============="""
def getNum(src, model):
img1 = cv2.imread(r"C:\Users\LEGION\Desktop\2a72c3384c9818424f5b46dea6bdb835.bmp")
img2 = cv2.imread(r"C:\Users\LEGION\Desktop\5cdb73f36e8f3d1d256bb6a5d9dc19a7.bmp")
sift = cv2.SIFT_create() # orb_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
flann = cv2.FlannBasedMatcher()
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
for file in os.listdir(r"E:\xwechat_files\wxid_qi43v1w2nqcb12_e432\msg\file\2026-01\database\database"):
model = os.path.join(r"E:\xwechat_files\wxid_qi43v1w2nqcb12_e432\msg\file\2026-01\database\database", file)
num = getNum(src, model)
print("文件名:", file, "匹配点个数:", num)
if num > max:
max = num
name = file
ID = name[0]
if max < 200: # src图片不一定是库里面人的指纹
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"
ID = getID(src, database)
name = getName(ID)
print("识别结果为:", name)
运行结果:

更多推荐
所有评论(0)