计算机视觉进阶教学之特征检测
目录
简介
一、harris角点检测
二、sift特征检测
三、指纹识别
1. 导入库和基础函数定义
2. 核心验证函数verification
2.1 特征提取
2.2 特征匹配
2.3 匹配筛选(Lowe's 比率测试)
2.4 认证判断
3. 主程序执行
简介
在计算机视觉的技术体系中,特征检测是连接图像底层像素与高层语义理解的核心桥梁,更是实现目标跟踪、图像匹配、三维重建等复杂任务的关键基石。无论是自动驾驶中的障碍物识别,还是无人机航拍的场景拼接,亦或是手机相机的智能美颜与防抖,背后都离不开高效、鲁棒的特征检测技术支撑。对于渴望从 “入门” 迈向 “进阶” 的计算机视觉学习者而言,吃透特征检测的原理、算法与实践,是突破技术瓶颈、提升工程能力的必经之路。
一、harris角点检测
Harris角点检测算法是一种常用的计算机视觉算法,用于检测图像中的角点。该算法通过计算图像中每个像素的局部自相关矩阵,来判断该像素是否为角点。
角点检测算法的基本思想:
使用一个固定的小窗口在图像上进行任意方向的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化(sobel算子),那么我们可以认为该窗口中存在角点。
# 角点指图像中局部区域与周围区域有较大灰度变化的点或像素。
# cornerHarris(img, blockSize, ksize, k[, dst[, borderType]]) -> dst
# • img: 输入图像。
# • blockSize: 角点检测中要考虑的邻域大小。
# • ksize: Sobel求导中使用的窗口大小。
# • k: Harris角点检测方程中的自由参数,取值参数为 [0.04, 0.06]。
# dst: 返回numpy.ndarray对象,大小和src相同,值越大,对应像素是角点的概率越高
img = cv2.imread('dama.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
dst = cv2.cornerHarris(gray, blockSize=4, ksize=3, k=0.04)
# 标记检测到的角点
img[dst > 0.05 * dst.max()] = [0, 0, 255]
# 这里通过对角点响应进行阈值处理,标记出检测到的角点
cv2.imshow('img', img)
cv2.waitKey(0)
dst = cv2.cornerHarris(gray, blockSize=4, ksize=3, k=0.04)
gray
:输入的灰度图像blockSize=4
:角点检测时考虑的邻域大小为 4x4ksize=3
:Sobel 算子的窗口大小为 3x3,用于计算图像梯度k=0.04
:Harris 检测方程中的自由参数,在 [0.04, 0.06] 范围内
img[dst > 0.05 * dst.max()] = [0, 0, 255]
- 找到
dst
中的最大值(最可能是角点的点) - 设定阈值为最大值的 5%(0.05 * dst.max ())
- 对于所有响应值大于该阈值的像素,在原始图像上将其颜色设置为红色 [0, 0, 255](注意 OpenCV 中颜色顺序是 BGR)
二、sift特征检测
SIFT(Scale Invariant Feature Transform)尺度不变特征变换。SIFT特征具有对旋转、尺度缩放、亮度变化等保持不变性,是一种非常稳定的局部特征。
SIFT算法具的特点:
- 1、图像的局部特征,对旋转、尺度缩放、亮度变化保持不变,对视角变化、仿射变换、噪声也保持一定程度的稳定性。
- 2、独特性好,信息量丰富,适用于海量特征库进行快速、准确的匹配。
- 3、多量性,即使是很少几个物体也可以产生大量的SIFT特征
- 4、高速性,经优化的SIFT匹配算法甚至可以达到实时性 5、扩招性,可以很方便的与其他的特征向量进行联合。
# 特征提取SIFT
# 检测图像中的关键点
# cv2.SIFT_create() #cv2.xfeatures2d.SIFT_create()#创建一个sift特征的提取对象
# sift.detect()img在图像中查找关键点man = cv2.imread('dama.jpg')
man_gray = cv2.cvtColor(man, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create() # SIFT对象
kp = sift.detect(man_gray)
# kp: 检测到的(x, y)
# 坐标。
# kp.size: 关键点的大小(尺度)。
# kp.angle: 关键点的方向。
# kp.response: 关键点的响应值。
# kp.octave: 关键点所在的金字塔层级。
# 绘制关键点
# drawKeypoints(image, keypoints, outImage, color=None, flags=None)
# image: 原始图片
# keypoints: 从原图中获得的关键点,这也是画图时所用到的数据
# outputImage: 输出图像,可以是原始图片,也可以是None
# color: 画笔的颜色,通过修改(b,g,r)的值,更改画笔的颜色,b=蓝色,g=绿色,r=红色。
# flags: 绘图功能的标识设置 绘制富有信息的关键点。man_sift = cv2.drawKeypoints(man, kp, outImage=None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow('man_sift', man_sift)
cv2.waitKey(0)# 使用sift.compute()计算关键点描述符,方便后期的特征匹配
kp, des = sift.compute(man, kp)
print(np.array(kp).shape, des.shape)
# 输出关键点的形状和描述符的形状。
# np.array(kp).shape 表示关键点的数量和属性。
# des.shape 表示描述符的数量和属性。
sift = cv2.SIFT_create() # 创建SIFT特征提取对象
kp = sift.detect(man_gray) # 检测图像中的关键点
-
cv2.SIFT_create()
:创建一个 SIFT 特征提取器实例 -
sift.detect()
:在灰度图像上检测关键点,返回一个包含所有关键点的列表每个关键点(
kp
中的元素)包含以下重要属性:kp.pt
:关键点的 (x, y) 坐标kp.size
:关键点的大小(尺度)kp.angle
:关键点的方向(0-360 度)kp.response
:关键点的响应值(响应值越高,关键点越显著)kp.octave
:关键点所在的金字塔层级(与尺度相关)
man_sift = cv2.drawKeypoints(man, kp, outImage=None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.drawKeypoints()
:在图像上绘制检测到的关键点- 参数说明:
man
:原始彩色图像kp
:之前检测到的关键点outImage=None
:输出图像,设为 None 表示函数会自动创建flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
:绘制 "丰富" 的关键点,不仅显示位置,还会显示关键点的尺度(圆圈大小)和方向(箭头)
三、指纹识别
使用OpenCV进行指纹识别是一个复杂且挑战性的任务,因为指纹识别通常需要高精度的特征提取和匹配算法。虽然OpenCV提供了多种图像处理和计算机视觉的工具,但直接使用OpenCV的内置功能(如SIFT、SURF、ORB等特征检测器)进行指纹识别可能并不总是足够有效。
下面代码实现了一个基于 SIFT 特征匹配的图像认证系统,通过比较源图像与模板图像的特征相似性来判断是否认证通过。
model.bmp/src1.bmp
src2.bmp
1. 导入库和基础函数定义
import os
import cv2def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0)
2. 核心验证函数verification
2.1 特征提取
# 创建SIFT特征提取器
sift = cv2.SIFT_create()
# 检测关键点和计算描述符(特征向量) 源图像
kp1, des1 = sift.detectAndCompute(src, None) # 第二个参数: 掩膜
# 检测关键点和计算描述符 模板图像
kp2, des2 = sift.detectAndCompute(model, None)
sift.detectAndCompute()
:同时完成关键点检测和描述符计算kp1
/kp2
:分别是源图像和模板图像的关键点列表des1
/des2
:分别是源图像和模板图像的特征描述符矩阵- 第二个参数
None
表示不使用掩膜(即处理整幅图像)
2.2 特征匹配
# 创建FLANN匹配器
flann = cv2.FlannBasedMatcher()
# 使用k近邻匹配(des1中的每个描述符与des2中的最近两个描述符进行匹配)
matches = flann.knnMatch(des1, des2, k=2)
- FLANN(Fast Library for Approximate Nearest Neighbors)是一种快速近似最近邻匹配算法,比暴力匹配更高效
knnMatch(k=2)
:为每个特征点在另一幅图像中寻找最近的 2 个匹配点
2.3 匹配筛选(Lowe's 比率测试)
# 进行比较筛选
ok = []
for m, n in matches:# 根据Lowe's比率测试,选择最佳匹配if m.distance < 0.8 * n.distance:ok.append((m, n))
- Lowe's 比率测试:如果最佳匹配(m)与次佳匹配(n)的距离比值小于阈值(这里是 0.8),则认为是好的匹配
- 原理:真正的匹配点通常具有明显优于其他匹配的距离值
ok
列表存储通过筛选的优质匹配对
2.4 认证判断
# 统计通过筛选的匹配数量
num = len(ok)
if num >= 500:result = "认证通过"
else:result = "认证失败"
return result
- 通过统计优质匹配的数量来判断图像是否匹配
- 这里设置阈值为 500,当匹配数超过该值时认为认证通
3. 主程序执行
if __name__ == "__main__":src1 = cv2.imread("src1.BMP")cv_show('src1', src1)src2 = cv2.imread("src2.BMP")cv_show('src2', src2)model = cv2.imread("model.BMP")cv_show('model', model)result1 = verification(src1, model)result2 = verification(src2, model)print("src1验证结果为:", result1)print("src2验证结果为:", result2)
- 读取两个源图像(src1.BMP、src2.BMP)和一个模板图像(model.BMP)
- 分别显示这三个图像
- 调用
verification
函数分别比较 src1 与 model、src2 与 model 的匹配程度 - 输出两个源图像的认证结果
根据上面那个基础的我们进行优化一下能够通过比较输入指纹与数据库中指纹的匹配程度,识别出对应的人员信息。
# """============计算两个指纹间匹配点的个数============="""
# 1个用法
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()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"""============获取指纹编号============="""
# 1个用法
def getID(src, database):max = 0for file in os.listdir(database):model = os.path.join(database, file)num = getNum(src, model)print("文件名:", file, "匹配点个数:", num)if num > max:max = numname = fileID = name[0]if max < 100: # src图片不一定是库里面人的指纹ID = 9999return ID"""============根据指纹编号,获取对应姓名============="""
# 1个用法
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)
主函数流程:
- 定义待识别指纹图像路径和数据库路径
- 调用
getID
函数获取最匹配的指纹 ID - 调用
getName
函数将 ID 转换为姓名 - 输出最终的识别结果
系统工作原理总结
- 输入一个待识别的指纹图像
- 与数据库中所有指纹图像逐一进行 SIFT 特征匹配
- 统计每个匹配的有效特征点数量
- 找到匹配点最多的指纹作为识别结果
- 通过预设的 ID - 姓名映射表,返回最终的人员姓名