注:本文选自中国水利水电出版社出版的《深度学习实战:基于TensorFlow2.X的计算机视觉开发应用 》一书,略有改动。经出版社授权刊登于此。
传统的基于细节点的指纹识别是通过一系列的图像处理和操作技术才能完成,随着近年来人工智能技术的不断发展,将卷积神经网络应用在指纹识别匹配上成为了可能。在本文章中,首先介绍了孪生神经网络的定义,之后会讲解如何利用Tensorflow2.X结合孪生神经网络来完成基于公开指纹数据集的指纹识别任务。
孪生神经网络
孪生神经网络英文简称为Siamese网络,它的主体结构是由一对共享相同权值的神经网络所组成的,通常被用于来计算数据之间的相似度,也正是因为具有相同的权值,因此才被称为“孪生”。值得注意的是,它并不是一个具体的神经网络模型,而只是一个架构,其中两边的输入网络可以被自定义设置为具体的CNN卷积神经网络或其它模型,其示意图如图1所示。
图1 孪生神经网络
虽然这里强调的是共享相同权值的一对神经网络才能被称为孪生神经网络,但事实上也可以让它们的权值各不相同,但这样的网络则有另一个称呼,即被称为伪孪生神经网络,其示意图如图2所示。孪生神经网络由于共享权值,因此适合于处理输入是一对较为类似或近似的数据的情况,这样共享的权重才能对两边的数据都有着较好的适应性。反之,如果输入的一对数据本身存在着较大的差异,例如图像和字幕之间的关联,那么即可以通过伪神经网络来进行处理。
图2 伪孪生神经网络
通常来说,输入到孪生神经网络里的一对数据最后都会被分别转换成一个向量的形式,之后根据这二者向量之间的差异来得到它们之间的相似度,例如常见的度量指标有欧氏距离和余弦距离等。整个网络的训练目标是让两个相似的输入距离尽可能的小,而让两个不同类别或差异较大的输入距离尽可能的大。以人脸识别为例,同一个人的脸被认为是相似输入,而不同人的人脸即被认为是不同输入,其示意图如图3所示。
图3 孪生神经网络的输入数据
另外可以计算输入图像相似度的网络不单单只有孪生神经网络和伪孪生神经网络,例如三输入的三元组Triplet网络同样也可以实现。虽然事实上分类网络也可以达到这样的效果,但通常来说它更多被应用于判断事物的总体分类,例如猫狗分类,而较少去关注个体分类,例如判断输入的猫是不是同一只猫。因此就目前来看,较为主流的相似度评判网络还是以孪生神经网络、伪孪生神经网络和三元组网络这三者为主。
将孪生神经网络应用到当前的指纹识别实战任务中去,即是将网络的输入数据替换成指纹图像。同一个人的指纹输入即为相似输入,不同人的指纹输入即为不同输入,而该网络的设计目的便是让它可以根据输入的指纹图像来判断这对指纹是否属于同一个人,即达到指纹识别的目的。通常其中一个输入可被视为是保存在数据库里的指纹模板,另一个输入则是当前用户访问服务时被采集到的指纹数据,其模型示意图如图4所示。
图4 基于孪生神经网络的指纹识别
CASIA指纹数据集
在本小节中,所使用的指纹数据来自于中国科学院生物特征与安全研究中心里的指纹数据库,其数据下载网站为: http://biometrics.idealtest.org/findTotalDbByMode.do?mode=Fingerprint
登录到网站之后,会看到如图5所示的页面,其中左侧上方是数据的分类,包含了常见的生物特征数据如虹膜、指纹、脸和掌纹等,而右侧则是当前所定位到的多个版本的指纹数据集,在本次实战中这里选用右侧最上方出现的指纹数据集即CASIA Fingerprint Subject Ageing Version 1.0.
图5 CASIA指纹数据集下载页面(1)
由于在该网站中下载相关数据集需要以个人账户的形式进行访问,因此需要事先注册一个账号。首先点击右上方Register按钮跳转至如图6所示页面,之后点击右下方的IAgree按钮并同意相关协议后跳转到如7所示的个人信息页面。在填报好相关内容之后,会对指定的个人邮箱发送验证信息,通过之后便可以拥有一个可以访问于该网站的个人账号。
图6 CASIA指纹数据集下载页面(2)
图7 CASIA指纹数据集下载页面(3)
登录账号并点击先前的CASIA Fingerprint Subject Ageing Version 1.0.数据集进行下载,下载完成并解压之后一共会得到2009和2013两个年份收集到的指纹数据集,其中具体文件结构如图8所示。
图8 CASIA指纹数据集文件结构
在图8中,从上至下,2009和2013代表的是指纹数据收集的年份,uru4000、T2和uru4500代表的是传感器的类型,1、2表示的是收集指纹的批数,1代表第一批,2代表第二批。而0000等数字表示的是被采集者的ID,最后获得到的数据格式会是诸如RRRR_IIIIF_XXXX_Z_S.bmp这样的形式,其不同序号表达的具体信息如图9所示。以图9.20所示的0403_00001_0000_0_S.bmp为例,0403表示的是当前采集这个指纹所使用的传感器ID(不是类型),第一个0000表示的是被采集者的ID,紧接其后的1表示的是采集的是右食指,第二个0000表示的是图像的索引序号,而最后一个0则是表示被采集的当前次数。
图9 CASIA指纹数据名称含义
整个CASIA指纹数据集一共由5880张指纹组成,它们分别采自于不同的实验对象,其中针对每个实验对象的左、右食指和中指进行一共20次指纹图像的采集,即每个手指分别会被采集5次,图10展示了CASIA数据中的一个试验者被采集到的左、右食指和中指的指纹图像。
图10 CASIA指纹数据集案例
指纹对齐实战
由于在CASIA指纹数据集中,采集到的指纹数据特征有可能各不相同,有些采集到的指纹特征甚至已经遭到破坏(如潮湿、刮伤等),因此分析这种包含各种真实情况的指纹数据集会更加具有实际意义。而在利用孪生神经网络对不同人的指纹进行匹配之前,最重要的一步那便是指纹的对齐操作。
由于在指纹采集的过程中手指会产生旋转、偏移或错位,因此采集到的指纹图像其中很多都是无法正常对齐的,特征点无法直接进行匹配,图10展示了一幅在CASIA数据集中同一个人相同手指在不同时间点时被采集到的指纹图像,从图中可以观察到即便被采集的是相同的手指,但所被标注的重点区域却是各不相同的且无法通过肉眼直接将它们对应起来。而倘若指纹识别系统没有对某个人验证时采集到的指纹进行对齐或矫正,那么该指纹就很难与储存在数据库中的指纹模板匹配起来,由此则会产生验证失败。
图10 同一个人相同手指被采集的指纹图像
针对指纹的对齐算法来说,目前应用较为广泛的其中一种是SIFT算法或SURF算法。SIFT算法全称为Scale-invariant feature transform,即尺度不变特征变换。而SURF则可以看作为是SIFT算法的一种改良,通过一种更高效的方式来完成特征的提取和描述,其全称为Speeded Up Robust Features,即加速稳健特征。由于在本实战中更注重的是神经网络模型的搭建和训练,因此这里仅仅只对SIFT特征点匹配算法进行简要的介绍,后续其可以通OpenCV第三方库来进行直接调用。
SIFT算法于1999年被David Lowe所提出,其之后被广泛应用于计算机视觉里的各个领域里,如图像识别、图像匹配和目标检测等,它是一种基于局部兴趣特征点的算法,不受图像尺度和旋转的影响,同时也对光照和外部因素有着很强的抗干扰性,因此适合被用作于图像的分析上,尤其是在目标检测领域里一直备受关注,即便是今日,该算法依旧被许多研究学者所采用。
SIFT算法主要可以被划分为2个步骤,即特征点的检测和特征点描述。在特征点检测阶段,SIFT会利用DoG(Difference of Gaussian)算法先进行角点检测,将一些变化明显的区域如边缘和角当作特征点,因为它们相对于平滑区域来说变化相对更加明显。之后在特征点描述的建立阶段,SIFT首先会计算得到图像局部区域的梯度直方图,并将它转换成一定维数的特征向量。之后在计算出特征向量后会再对领域的特征向量进行归一化处理并计算出该区域关键点的主方向,而领域的特征可以根据主方向进行特定方向的旋转,使其具有旋转不变性,同样地,也可以根据领域中各像素的大小进行指定尺度的缩放,从而实现尺度不变性。
旋转不变性和尺度不变性是图像分类和目标检测等任务中最重要的两个性质,而迁移到图像对齐任务中来,同样也十分重要。以刚才提到的指纹对齐为例,在利用了SIFT算法后,即便采集到的是旋转、偏移的指纹图像也能和数据库中的指纹模板进行匹配,而其中原因便是SIFT算法具有旋转不变性和尺度不变性,能够从这些有着旋转、偏移的指纹图像中提取出正确的特征点,之后根据输入指纹的特征点位置与数据库存储的指纹模板的特征点位置进行比较和矫正,便可完成指纹的对齐工作。
对于SIFT算法,可以直接从旧版本的OpenCV第三方库中调用得到,这里演示如何利用SIFT算法对CASIA指纹数据集进行个人指纹的对齐操作。首先导入相关的第三方库:
代码语言:javascript复制import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
定义利用OpenCV库处理指纹并进行对齐的函数。整体流程是先将读取到的2张匹配指纹图像转换成灰度图像,之后实例化SIFT算法函数并检测2张指纹图像的关键点和描述符。在得到相关特征后再利用常见的二维特征点匹配算法Brute Force将它们进行匹配和排序,对于匹配得分较差的关键点则舍去,只选取匹配得分前20%的匹配结果,并用直线画出两张图像的关键点匹配关系。最后再提取出高匹配得分的关键点位置,计算两张图像之间的单应性矩阵,并对用户的输入指纹进行仿射变换以至可以和数据库里的指纹模板对齐。
代码语言:javascript复制def alignImages(im1, im2):
im1Gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY) #将图片转换为灰度图
im2Gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create() #调用SIFT算法
keypoints1, descriptors1 = sift.detectAndCompute(im1Gray, None) #检测特征点和描述符
keypoints2, descriptors2 = sift.detectAndCompute(im2Gray, None)
matcher = cv2.BFMatcher() #调用Brute Force匹配算法
matches = matcher.match(descriptors1, descriptors2, None) #对两张指纹图像进行匹配
matches.sort(key=lambda x: x.distance, reverse=False) #将匹配分数进行排序
numGoodMatches = int(len(matches)*0.2) #选取排序得分前20%的指纹
matches = matches[:numGoodMatches]
#绘制匹配点
imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None)
cv2.imwrite("matches.jpg", imMatches) #保存匹配图像
points1 = np.zeros((len(matches), 2), dtype=np.float32)
points2 = np.zeros((len(matches), 2), dtype=np.float32)
for i, match in enumerate(matches): #提取匹配点的位置
points1[i, :] = keypoints1[match.queryIdx].pt
points2[i, :] = keypoints2[match.trainIdx].pt
h, mask = cv2.findHomography(points1, points2, cv2.RANSAC) #寻找仿射矩阵
height, width, channels = im2.shape
im1Reg = cv2.warpPerspective(im1, h, (width, height)) #对匹配图像进行对齐
return im1Reg, h
这里随机选取数据集中同一个人的相同手指的不同指纹图像进行对齐测试,以后缀名为000_1_S的指纹图像作为对齐标准,并输出它们之间的单适应矩阵:
代码语言:javascript复制refFilename = "0403_00032_0001_1_S.bmp"
imReference = cv2.imread(refFilename, cv2.IMREAD_COLOR) #读取图像
imFilename = "0403_00032_0002_2_S.bmp"
im = cv2.imread(imFilename, cv2.IMREAD_COLOR)
imReg, h = alignImages(im, imReference) #对齐图像
outFilename = "aligned.bmp" #存储对齐图像的名称
cv2.imwrite(outFilename, imReg) #保存对齐图像
print("Estimated homography : n", h) #输出单适应矩阵
输出的结果为:
代码语言:javascript复制Estimated homography :
[[1.01314862e 00 1.64195638e-03 8.18305996e 00]
[1.54099247e-02 1.03239412e 00 4.24797103e 01]
[5.97959741e-05 3.12206003e-05 1.00000000e 00]]
查看对齐前后的指纹图像并与待匹配的指纹图像作对比,如图11所示。
图11 指纹对齐案例
可以看出在对齐之前,输入的指纹图像与待匹配图像从位置上无法一一对应,而在对齐之后,以如图11所示的指纹中间的螺旋纹为标准,可以看出两个指纹已成功完成对齐操作。但是很明显可以看出在执行SIFT算法之后,生成的对齐图像缺失了一部分图像信息,这部分信息是原图之前所没有的,在SIFT算法里会直接将它们填充成无关的背景色。另外由于在先前指纹对齐的函数中标注了图像之间关键点的对应关系,因此还可以可视化它们之间特征关键点的匹配过程,其示意图如图12所示。
图12 指纹对齐的对应关键点匹配
之后将SIFT算法应用于CASIA数据集上,作为实战案例这里只选取了其中一小部分数据集进行指纹识别项目演示。以同一个人相同手指的第一张指纹图像作为对齐标准,剩下的四张指纹图像作为待对齐的图像。首先需要生成指定要求的对应文件路径,方便后续进行对齐并加载保存:
代码语言:javascript复制#读取数据集路径
dir_path = r'D:Project_codefingerPrintCASIA_Fingerprint_Subject_Ageing2013uru40002'
folder_list = os.listdir(dir_path) #查看该路径下文件夹信息
#得到每个文件夹的具体路径
imagePath_list = [os.path.join(dir_path, imageName) for imageName in folder_list]
imageAlignedPath = []
for i in imagePath_list:
firstIndex = 0
imageName_list = os.listdir(i) #遍历每个文件夹下的图片名称
for j in range(len(imageName_list)):
if j == firstIndex:
continue
elif j%5==0:
firstIndex =5
else:
image1Path_list = [os.path.join(i, imageName_list[firstIndex])]
imageAlignedPath.append(image1Path_list)
image2Path_list = [os.path.join(i, imageName_list[j])]
#准备待对齐的指纹路径,格式为前面一张为指纹对齐参考图像,后一张为待对齐图像,不断循环
imageAlignedPath.append(image2Path_list)
读取制作好的指纹图像文件路径,调用SIFT算法对它们进行一一匹配并保存至指定文件夹中:
代码语言:javascript复制i = 0
name = 1
while i < len(imageAlignedPath):
reference = cv2.imread(imageAlignedPath[i][0], cv2.IMREAD_COLOR) #读取指纹对齐参考图像
aligned = cv2.imread(imageAlignedPath[i 1][0], cv2.IMREAD_COLOR) #读取待对齐的图像
aligned_out, h = alignImages(reference, aligned) #对齐图像
reference_outName = imageAlignedPath[i][0][-23:-4] #读取指纹对齐参考图像文件名
aligned_outName = imageAlignedPath[i 1][0][-23:-4] #读取指纹待对齐图像的文件名
#参考图像和对齐图像的存储路径
save_path1 = r'D:Project_codefingerPrintdatasetGenuine(6)unaligned_soucereference'
save_path2 = r'D:Project_codefingerPrintdatasetGenuine(6)unaligned_soucealigned'
print("Reference image : ", reference_outName)
print("Saving aligned image : ", aligned_outName)
reference_savePath = save_path1 "\" str(name) '.jpg'
aligned_savePath = save_path2 "\" str(name) '.jpg'
cv2.imwrite(reference_savePath,reference) #存储指纹对齐参考图像
cv2.imwrite(aligned_savePath,aligned_out) #存储已对齐的指纹图像
i =2
name =1
输出的结果为:
代码语言:javascript复制Reference image : 0403_00001_0000_0_S
Saving aligned image : 0403_00001_0001_1_S
Reference image : 0403_00001_0000_0_S
Saving aligned image : 0403_00001_0002_2_S
Reference image : 0403_00001_0000_0_S
Saving aligned image : 0403_00001_0003_3_S
Reference image : 0403_00001_0000_0_S
Saving aligned image : 0403_00001_0004_4_S
……….
打开文件夹查看指纹图像的对齐结果,如图13所示。
图13 CASIA指纹数据集部分数据对齐结果
从对齐结果上来看,可以发现有小部分的指纹在对齐过程中产生了失败,生成出了奇怪的结果,这主要是由于两个指纹图像在对齐过程中没有找到足够的关键特征点来进行匹配。对于这部分图像需要事先人为地将它们删除,以保证这些离群数据不会影响到后续模型训练时的可靠性和准确性,经过人工处理后的成对指纹图像数据集如图14所示。
图14 待匹配指纹
指纹识别模型搭建与训练
在对指纹数据集进行对齐和一定的预处理操作之后,下一步便是将这些成对的指纹图像送入到孪生神经网络中以便于完成指纹识别任务的训练,在本实战中待匹配指纹图像和指纹模板图像各自选取278张,其中139张为同一个人相同手指的指纹,139张为不同人相同手指的指纹。这里首先导入相关的第三方库,其中tensorflow.keras.preprocessing库用于图像后续的读取:
代码语言:javascript复制import tensorflow as tf
from tensorflow import keras
import cv2
import os
import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras import backend as K
from tensorflow.keras.preprocessing import image
定义图像存储路径,其中带有Aligned名称的路径表示对齐后的待匹配指纹图像存储位置,而带有Reference名称的路径则表示对应的指纹模板图像存储位置,具体的路径可以根据个人喜好再进行更改。
代码语言:javascript复制Train_referencePath = r'D:Project_codefingerPrintdatasetGenuine(2)Reference'
Train_alignedPath = r'D:Project_codefingerPrintdatasetGenuine(2)Aligned'
读取路径下的图像数据,并将它们储存至列表当中,方便模型后续处理:
代码语言:javascript复制referenceTrain = []
alignedTrain = []
Train_sourceImageList = os.listdir(Train_referencePath)
Train_sourceImageList.sort(key=lambda x:int(x[:-4]))
Train_alignedImageList = os.listdir(Train_alignedPath)
Train_alignedImageList.sort(key=lambda x:int(x[:-4]))
for i in Train_sourceImageList:
imagePath = os.path.join(Train_referencePath,i) #读取reference图像路径
referenceImage = image.load_img(imagePath) #加载图像成jpg格式
img_array = image.img_to_array(referenceImage) #转换成array格式
referenceTrain.append(img_array) #保存至列表
for i in Train_alignedImageList:
imagePath = os.path.join(Train_alignedPath,i)
alignedImage = image.load_img(imagePath)
img_array = image.img_to_array(alignedImage)
alignedTrain.append(img_array)
将列表形式转换为numpy数组:
代码语言:javascript复制print("Original type:%s"%type(referenceTrain))
referenceTrain = np.array(referenceTrain) #转换图像类型
alignedTrain = np.array(alignedTrain)
print("Converted type:%s"%type(referenceTrain))
输出的结果为:
代码语言:javascript复制Original type:<class 'list'>
Converted type:<class 'numpy.ndarray'>
转换图像类型并进行归一化:
代码语言:javascript复制referenceTrain = referenceTrain.astype('float32')/255 #图像归一化
alignedTrain = alignedTrain.astype('float32')/255
查看图像的尺寸:
代码语言:javascript复制print(alignedTrain[-1].shape) #查看图像尺寸
输出的结果为:
代码语言:javascript复制(356, 328, 3)
制作数据标签,其中1代表同一个人相同手指的指纹,0代表不同人相同手指的指纹,之后将它们也转换成numpy数组形式:
代码语言:javascript复制labels = [1]*139 [0]*139 #制作图像标签
labels = np.array(labels).astype('float32') #标签类型转换
print(type(labels))
输出的结果为:
代码语言:javascript复制<class 'numpy.ndarray'>
定义以欧氏距离为基础的输入图像对的距离衡量函数,对抗损失函数以及准确率指标计算函数:
代码语言:javascript复制def euclidean_distance(vects): #向量欧式距离计算
x, y = vects
sum_square = K.sum(K.square(x - y), axis=1, keepdims=True)
return K.sqrt(K.maximum(sum_square, K.epsilon()))
def eucl_dist_output_shape(shapes): #结果输出维度
shape1, shape2 = shapes
return (shape1[0], 1)
def contrastive_loss(y_true, y_pred): #对抗损失函数
margin = 1
sqaure_pred = K.square(y_pred)
margin_square = K.square(K.maximum(margin - y_pred, 0))
return K.mean(y_true * sqaure_pred (1 - y_true) * margin_square)
def accuracy(y_true, y_pred): #模型训练评判指标
return K.mean(K.equal(y_true, K.cast(y_pred <0.5, y_true.dtype)))
定义孪生神经网络的模型架构,这里采用多尺度空洞卷积组合所构成的神经网络,该模型已被相关文献证明是一个能够提取不同尺度的指纹细节点特征的匹配网络结构。其中模型示意图如图15所示:
图15 多尺度空洞卷积神经网络
其模型代码如下所示,其中卷积层里的dilation_rate表示的是空洞卷积的扩张率,经过不断的前向传播,最后模型会输出一个向量值用来衡量图像间的距离和相似度:
代码语言:javascript复制def MDCNN_network(input_shape): #模型搭建
input = keras.layers.Input(shape = input_shape)
x = keras.layers.Conv2D(filters=2,kernel_size=3,input_shape=(356,328,3))(input)
x = keras.activations.selu(x)
x1 = keras.layers.Conv2D(filters=4,kernel_size=3,strides=1,padding='same')(x)
x1 = keras.activations.selu(x1)
x2 = keras.layers.Conv2D(filters=4,kernel_size=3,strides=1,dilation_rate=(2,2),padding='same')(x)
x2 = keras.activations.selu(x2)
x3 = keras.layers.Conv2D(filters=4,kernel_size=3,strides=1,dilation_rate=(3,3),padding='same')(x)
x3 = keras.activations.selu(x3)
x = keras.layers.Add()([x1,x2,x3])
x = keras.layers.Conv2D(filters=8,kernel_size=3)(x)
x = keras.activations.selu(x)
x1 = keras.layers.Conv2D(filters=16,kernel_size=3,strides=2)(x)
x1 = keras.activations.selu(x1)
x2 = keras.layers.Conv2D(filters=16,kernel_size=3,strides=2)(x)
x2 = keras.activations.selu(x2)
x3 = keras.layers.Conv2D(filters=16,kernel_size=3,strides=2)(x)
x3 = keras.activations.selu(x3)
x = keras.layers.Add()([x1,x2,x3])
x = keras.layers.Conv2D(filters=32,kernel_size=3)(x)
x = keras.activations.selu(x)
x = keras.layers.Conv2D(filters=64,kernel_size=3,strides=2)(x)
x = keras.activations.selu(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(10)(x)
return keras.models.Model(input, x)
利用先前定义过的距离函数,将模型的输出转换成欧式距离上的衡量,并将它拼接至搭建的模型上:
代码语言:javascript复制base_network = MDCNN_network(sourceTrain[0].shape)
input_a = keras.Input(shape=sourceTrain[0].shape)
input_b = keras.Input(shape=alignedTrain[0].shape)
processed_a = base_network(input_a) #模型一路输入
processed_b = base_network(input_b) #模型另一路输入
distance = keras.layers.Lambda(euclidean_distance,output_shape=eucl_dist_output_shape)([processed_a, processed_b])
model = keras.models.Model([input_a, input_b], distance) #模型拼接
配置模型的损失函数、优化器、评价指标等相关训练参数:
代码语言:javascript复制model.compile(loss=contrastive_loss,optimizer='adam',metrics=[accuracy])
对模型进行训练:
代码语言:javascript复制history=model.fit([sourceTrain, alignedTrain],labels,batch_size=1,epochs=10,verbose=1)
输出的结果为:
代码语言:javascript复制………
Epoch 5/10
278/278 [==============================] - 144s 517ms/sample - loss: 0.3579 - accuracy: 0.7734
Epoch 6/10
278/278 [==============================] - 152s 547ms/sample - loss: 0.1926 - accuracy: 0.8309
Epoch 7/10
278/278 [==============================] - 133s 478ms/sample - loss: 0.1012 - accuracy: 0.9065
Epoch 8/10
278/278 [==============================] - 137s 494ms/sample - loss: 0.0706 - accuracy: 0.9245
Epoch 9/10
278/278 [==============================] - 138s 498ms/sample - loss: 0.0407 - accuracy: 0.9676
Epoch 10/10
278/278 [==============================] - 137s 494ms/sample - loss: 0.0271 - accuracy: 0.9820
从CASIA数据集中随机准备四张非训练集的图像用于测试,其中两张图像为同一个人相同手指的指纹,其余两张图像为不同人不同手指或相同人不同手指的指纹,并将它们统一放至到代码的当前目录下。定义图像读取和处理函数,之后进行读取和输出模型的预测分数:
代码语言:javascript复制def testImage_preprocessing(image_path): #测试图像预处理函数
testImage = image.load_img(image_path) #图像加载
plt.imshow(testImage)
plt.show()
testImage = image.img_to_array(testImage).astype('float32')/255
testImage = np.expand_dims(testImage,axis=0) #图像维度增添以满足模型输入要求
return testImage
same_path_1 = r'1.png'
same_path_2 = r'2.png'
different_path_1 = '3.png'
different_path_2 = '4.png'
same_testImage_1 = testImage_preprocessing(same_path_1)
same_testImage_2 = testImage_preprocessing(same_path_2)
different_testImage_1 = testImage_preprocessing(different_path_1)
different_testImage_2 = testImage_preprocessing(different_path_2)
sameScore = model.predict([same_testImage_1,same_testImage_2]) #相同指纹距离预测
differentScore = model.predict([different_testImage_1,different_testImage_2]) #不同指纹距离预测
print("The distance of same fingerprint:%f"%sameScore[0][0])
print("The distance of different fingerprint:%f"%differentScore[0][0])
输出的结果如图16,图17所示:
图16 用于测试的相同指纹图像
图17 用于测试的不同指纹图像
代码语言:javascript复制The distance of same fingerprint:0.150276 #相同指纹距离小
The distance of different fingerprint:1.036739 #不同指纹距离大
倘若使用可视化的激活热力图Grad-CAM还能观察到在匹配的过程中,卷积神经网络提取的用于匹配的特征其实主要也是集中在先前所提到的指纹细节点上,其示意图如图18所示,而这一观察结果也再次证明了细节点特征在指纹识别中的重要性。
图18 热力图激活的指纹特征比较结果