OpenCV实例(五)指纹识别
OpenCV实例(五)指纹识别
作者:Xiou
1.指纹识别概述
1.1概述
指纹识别,简单来说就是判断一枚未知的指纹属于一组已知指纹里面的哪个人的指纹。这个识别过程与我们在村口识别远处走来的人类似,首先,要抓住主要特征,二者的主要特征要一致;其次,二者要有足够多的主要特征一致。满足了这两个条件就能判断一枚指纹是否与某个人的指纹一致了。
图像处理过程中非常关键的一个步骤就是特征提取。特征提取需要解决的问题有如下两个:
● 选择有用的特征。该过程要选择核心的关键特征,该特征要能体现当前图像的个性。
● 将特征量化。特征是抽象的,是计算机无法理解的,要把特征转换成数值的形式,以便通过计算完成图像的识别、匹配等。
图像的个性化特征,是指能够体现图像自身特点的、易于区别于其他图像的特征。个性化特征既可以是本类图像的专有特征,也可以是图像的通用特征。例如,在进行指纹识别时,可以采用两种不同的方式提取个性化特征:
● 提取指纹的专有特征,如脊线的方向、分叉点、顶点等。这些特征是针对指纹图像设计的。
● 提取图像中的关键点特征。关键点特征并不是每类图像专有的特征。例如,一些角点、拐点等形态或方向特征,提取指纹图像中这些关键点特征的方式与提取其他类型图像的关键点特征的方式没有区别。在处理其他类型的图像时,可以采用提取指纹图像关键点特征的方式将这些关键点特征提取出来,并在识别、比较等场景中使用。
1.2原理
在指纹识别过程中,非常关键的一个步骤是对指纹特征的处理。
通常情况下,要先提取已知指纹的特征,并将其存储在模板库中,以便在后续进行指纹识别时与待识别指纹特征进行比对。录入指纹特征的过程如图所示。
识别指纹时,可能存在如下两种情况。
● 一对一验证。此时,主要验证当前指纹是否和已知指纹一致,如利用指纹解锁手机。
● 一对多识别。此时,主要识别当前指纹和指纹库众多指纹中的哪个指纹基本一致,如单位的打卡系统。
2.指纹识别算法
基于指纹特征的指纹识别方法是一种非常传统的方法。该方法主要关注指纹自身的个性化特征,希望通过计算指纹个性化特征差异实现指纹识别。本节将主要介绍指纹的个性化特征表示、提取及基于指纹特征的指纹识别方法。
2.1特征提取
1.关键点类型的确定通常情况下,使用相交数(Crossing Number)判断关键点的类型。
2.关键点角度的确定针对不同类型的关键点采用不同的方式确定其角度。
标准化文件ISO/IEC 19794-2对指纹的关键点(Finger Minutiae Data)进行了细致的约定,可以参考该文件获取更多细节信息。
2.2MCC匹配方法
获得上述细节信息后,可以对指纹关键点特征进行进一步编码,以进行比较判断,从而实现指纹的验证、识别功能。
2010年,意大利博洛尼亚大学的Raffaele Cappelli等人提出了MCC(Minutia Cylinder-Code,细节点柱形编码),该编码旨在更有效地完成指纹识别过程中的关键点特征表示和匹配。
MCC是一种基于3D数据结构(称为圆柱体)的表示方式,该圆柱体由关键点的位置和方向构建。也可以说,MCC存储了关键点的距离、方向信息,并对此进行了重构,保证了关键点具有旋转、平移不变性。
MCC是非常有影响力非常好用的特征。在实践中,可以直接用该算法提取指纹特征,也可以根据需要提取更有特色的特征。从该特征的提取过程可以看到,在获取特征值时要尽可能让特征值具有如下特点。
● 包含更多信息:不仅要包含当前关键点的特征,还要尽可能包含周围更多关键点的特征。这样的特征信息量大,更能体现图像自身特点。或者说,这样的特征包含了图像内许多关键点的特征。
● 具有健壮性:特征在旋转、缩放、模糊等操作前后能够保持一致性,如SIFT特征。
2.3尺度不变特征变换(SIFT)
SIFT(Scale Invariant Feature Transform,SIFT)特征是一种与图像的大小和旋转无关的关键点特征,该特征不仅仅适用于指纹图像,还适用于其他图像。
SIFT特征的关键特性是与图像的大小和旋转无关,同时对于光线、噪声等不敏感(具有较好的健壮性)。
SIFT算法描述的特征能够很方便地被提取出来,同时具有极强的独特性(显著性),即使在海量的数据中也很容易被辨识,不易发生误认。同时SIFT算法可以通过对局部特征的辨识完成整体图像的确认,该特点对于辨识局部被遮蔽的物体非常有效。上述优点使得SIFT算法适用于在简单的硬件设备下实现指纹识别。
SIFT主要包含如下三个步骤。
Step 1:尺度空间变换。该步骤使图像在尺寸大小、清晰度上发生变换,旨在找到变换前后稳定存在的特征。Step 2:关键点定位。该步骤旨在找到局域范围内的极大值、极小值,并将这些极值点作为关键点。
Step 3:通过方向描述关键点。该步骤首先找到图像的方向,然后通过该方向确定每一个关键点邻域内像素点的方向,并将该方向集合作为当前关键点的特征值。
3.显示指纹的关键点
(1)Step 1:实例化SIFT特征。OpenCV中的函数SIFT_create可实现实例化。SIFT的相关功能在OpenCV的贡献包内,使用SIFT需要通过pip在Anaconda Prompt(或Windows命令行提示符窗口)内安装贡献包,具体为
SIFT的专利权于2020年3月6日到期,因此在之前的一段时间内,OpenCV不包含SIFT的相关功能。(2)Step 2:找出图像中的关键点,并计算关键点对应的SIFT特征向量。OpenCV中的函数detectAndCompute可完成检测和计算关键点的功能,其语法格式为:
通常情况下,对掩模没有要求。
其中,返回值“关键点描述符”为128维向量组成的列表;“关键点”为关键点列表,每个元素为一个关键点(KeyPoint),其包含信息如下:
● pt:关键点的坐标。
● size:描述关键点的区域。
● angle:角度,表示关键点的方向。
● response:响应程度,代表该关键点特征的独特性,越高越好。
● octave:表示从金字塔哪一层提取的数据。
● class_id:当要对图像进行分类时,可以通过该参数对每个关键点进行区分,未设定时为-1。
(3)Step 3:打印、可视化关键点。打印关键点时直接使用print语句即可。可视化关键点时,在OpenCV中使用函数drawKeypoints实现关键点绘制,其语法格式为:
代码实例:
# -*- coding: utf-8 -*-
import numpy as np
import cv2
#==========读取、显示指纹图像============
fp= cv2.imread("fingerprint.png")
cv2.imshow("fingerprint",fp)
#==========SIFT==================
sift = cv2.SIFT_create() #SIFT专利已经到期,可以正常使用。要安装贡献包:opencv-contrib-python
kp, des = sift.detectAndCompute(fp, None)
#==========绘制关键点==================
cv2.drawKeypoints(fp,kp,fp)
#==========显示关键点信息、描述符==================
print("关键点个数:",len(kp)) #显示kp的长度
print("前五个关键点:",kp[:5]) #显示前5条数据
print("第一个关键点的坐标:",kp[0].pt)
print("第一个关键点的区域:",kp[0].size)
print("第一个关键点的角度:",kp[0].angle)
print("第一个关键点的响应:",kp[0].response)
print("第一个关键点的层数:",kp[0].octave)
print("第一个关键点的类id:",kp[0].class_id)
print("描述符形状:",np.shape(des)) #显示des的形状
print("第一个描述符:",des[0]) #显示des[0]的值
#==========可视化关键点==================
cv2.imshow("points",fp)
cv2.waitKey()
cv2.destroyAllWindows()
输出结果:
4.基于SIFT的指纹识别
代码实例:
# -*- coding: utf-8 -*-
import os
import cv2
#===============计算两个指纹间匹配点的个数====================
def getNum(src, model):
img1 = cv2.imread(src)
img2 = cv2.imread(model)
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
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(database):
model = os.path.join(database, file)
num = getNum(src, model)
print("文件名:",file,"距离:",num)
if max < num:
max = num
name = file
ID=name[:1]
if max < 100:
ID= 9999
return ID
#==========根据指纹编号,获取对应姓名==============
def getName(ID):
nameID={0:'孙悟空',1:'猪八戒',2:'红孩儿',3:'刘能',4:'赵四',5:'杰克',
6:'杰克森',7:'tonny',8:'大柱子',9:'翠花',9999:"没找到"}
name=nameID.get(int(ID))
return name
#==============主函数====================
if __name__ == "__main__":
src=r"identification/src.bmp"
database=r"identification/database"
ID=getID(src,database)
name=getName(ID)
print("识别结果为:",name)
输出结果: