0.引言
利用python开发,借助dlib库捕获摄像头中的人脸,提取人脸特征,通过计算欧氏距离来和预存的人脸特征进行对比,达到人脸识别的目的;
可以自动从摄像头中抠取人脸图片存储到本地,然后提取构建预设人脸特征;
根据抠取的 / 已有的同一个人多张人脸图片提取128d特征值,然后计算该人的128d特征均值;
然后和摄像头中实时获取到的人脸提取出的特征值,计算欧氏距离,判定是否为同一张人脸;
人脸识别 / face recognition的说明:
wikipedia 关于人脸识别系统 / face recognition system 的描述:theywork by comparing selected facial featuresfrom given image with faces within a database.
本项目中就是比较 预设的人脸的特征和 摄像头实时获取到的人脸的特征;
核心就是提取128d人脸特征,然后计算摄像头人脸特征和预设的特征脸的欧式距离,进行比对;
效果如下(摄像头认出来我是default_person预设的人脸 / 另一个人不是预设人脸显示diff):
图1 摄像头人脸识别效果gif
1.总体流程
先说下 人脸检测 (face detection) 和 人脸识别 (face recognition) ,前者是达到检测出场景中人脸的目的就可以了,而后者不仅需要检测出人脸,还要和已有人脸数据进行比对,识别出是否在数据库中,或者进行身份标注之类处理,人脸检测和人脸识别两者有时候可能会被理解混淆;
我的之前一些项目都是用dlib做人脸检测这块,这个项目想要实现的功能是人脸识别功能,借助的是 dlib官网中 face_recognition.py这个例程 (link:http://dlib.net/face_recognition.py.html);
核心在于 利用 “dlib_face_recognition_resnet_model_v1.dat” 这个model,提取人脸图像的128d特征,然后比对不同人脸图片的128d特征,设定阈值计算欧氏距离来判断是否为同一张脸;
1
2
3
4
5
|
# face recognition model, the object maps human faces into 128d vectors facerec = dlib.face_recognition_model_v1( "dlib_face_recognition_resnet_model_v1.dat" ) shape = predictor(img, dets[ 0 ]) face_descriptor = facerec.compute_face_descriptor(img, shape) |
图2 总体设计流程
2.源码介绍
主要有
- get_face_from_camera.py ,
- get_features_into_csv.py,
- face_reco_from_camera.py
这三个py文件;
2.1get_face_from_camera.py / 采集构建xxx人脸数据
人脸识别需要将 提取到的图像数据 和已有图像数据进行比对分析,所以这个py文件实现的功能就是采集构建xxx的人脸数据;
程序会生成一个窗口,显示调用的摄像头实时获取的图像(关于摄像头的调用方式可以参考我的另一博客 http://www.zzvips.com/article/133112.html);
按s键可以保存当前视频流中的人脸图像,保存的路径由 path_save = “xxxx/get_from_camera/” 规定;
按q键退出窗口;
摄像头的调用是利用opencv库的cv2.videocapture(0), 此处参数为0代表调用的是笔记本的默认摄像头,你也可以让它调用传入已有视频文件;
图3get_face_from_camera.py 的界面
这样的话,你就可以在 path_save指定的目录下得到一组捕获到的人脸;
图4 捕获到的一组人脸
源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
# 2018-5-11 # by timestamp # cnblogs: http://www.cnblogs.com/adaminxie import dlib # 人脸识别的库dlib import numpy as np # 数据处理的库numpy import cv2 # 图像处理的库opencv # dlib预测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor( 'shape_predictor_68_face_landmarks.dat' ) # 创建cv2摄像头对象 cap = cv2.videocapture( 0 ) # cap.set(propid, value) # 设置视频参数,propid设置的视频参数,value设置的参数值 cap. set ( 3 , 480 ) # 截图screenshoot的计数器 cnt_ss = 0 # 人脸截图的计数器 cnt_p = 0 # 保存 path_save = "f:/code/python/p_dlib_face_reco/data/get_from_camera/" # cap.isopened() 返回true/false 检查初始化是否成功 while cap.isopened(): # cap.read() # 返回两个值: # 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾 # 图像对象,图像的三维矩阵q flag, im_rd = cap.read() # 每帧数据延时1ms,延时为0读取的是静态帧 kk = cv2.waitkey( 1 ) # 取灰度 img_gray = cv2.cvtcolor(im_rd, cv2.color_rgb2gray) # 人脸数rects rects = detector(img_gray, 0 ) # print(len(rects)) # 待会要写的字体 font = cv2.font_hershey_simplex if ( len (rects) ! = 0 ): # 检测到人脸 # 矩形框 for k, d in enumerate (rects): # 计算矩形大小 # (x,y), (宽度width, 高度height) pos_start = tuple ([d.left(), d.top()]) pos_end = tuple ([d.right(), d.bottom()]) # 计算矩形框大小 height = d.bottom() - d.top() width = d.right() - d.left() # 根据人脸大小生成空的图像 im_blank = np.zeros((height, width, 3 ), np.uint8) im_rd = cv2.rectangle(im_rd, tuple ([d.left(), d.top()]), tuple ([d.right(), d.bottom()]), ( 0 , 255 , 255 ), 2 ) im_blank = np.zeros((height, width, 3 ), np.uint8) # 保存人脸到本地 if (kk = = ord ( 's' )): cnt_p + = 1 for ii in range (height): for jj in range (width): im_blank[ii][jj] = im_rd[d.top() + ii][d.left() + jj] print (path_save + "img_face_" + str (cnt_p) + ".jpg" ) cv2.imwrite(path_save + "img_face_" + str (cnt_p) + ".jpg" , im_blank) cv2.puttext(im_rd, "faces: " + str ( len (rects)), ( 20 , 50 ), font, 1 , ( 0 , 0 , 255 ), 1 , cv2.line_aa) else : # 没有检测到人脸 cv2.puttext(im_rd, "no face" , ( 20 , 50 ), font, 1 , ( 0 , 0 , 255 ), 1 , cv2.line_aa) # 添加说明 im_rd = cv2.puttext(im_rd, "s: save face" , ( 20 , 400 ), font, 0.8 , ( 255 , 255 , 255 ), 1 , cv2.line_aa) im_rd = cv2.puttext(im_rd, "q: quit" , ( 20 , 450 ), font, 0.8 , ( 255 , 255 , 255 ), 1 , cv2.line_aa) # 按下q键退出 if (kk = = ord ( 'q' )): break # 窗口显示 cv2.imshow( "camera" , im_rd) # 释放摄像头 cap.release() # 删除建立的窗口 cv2.destroyallwindows() |
2.2get_features_into_csv.py / 提取特征存入csv
已经得到了xxx的一组人脸图像,现在就需要把他的面部特征提取出来;
这里借助 dlib 库的 face recognition model 人脸识别模型;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# face recognition model, the object maps human faces into 128d vectors facerec = dlib.face_recognition_model_v1( "dlib_face_recognition_resnet_model_v1.dat" ) # detector to find the faces detector = dlib.get_frontal_face_detector() # shape predictor to find the face landmarks predictor = dlib.shape_predictor( "shape_predictor_5_face_landmarks.dat" ) # 读取图片 img = io.imread(path_img) img_gray = cv2.cvtcolor(img, cv2.color_bgr2rgb) dets = detector(img_gray, 1 ) shape = predictor(img_gray, dets[ 0 ]) face_descriptor = facerec.compute_face_descriptor(img_gray, shape) |
我们可以看下对于某张图片,face_descriptor的输出结果:
绿色框内是我们的返回128d特征的函数;
在红色框内调用该函数来计算img_face_13.jpg;
可以看到黄色框中的输出为128d的向量;
图5 返回单张图像的128d特征的计算结果
所以我们就可以把path_save中的图像,进行批量的特征计算,然后写入csv中(利用 write_into_csv函数),我这边csv的命名为default_person.csv;
就可以得到行数(人脸数)*128列的一个特征csv;
这是某个人的人脸特征,然后计算128d特征的均值,求mean(利用 compute_the_mean函数)
运行的输出结果,这个128d的特征值,就是default_person的特征;
也就是我们内置/预设的人脸,之后摄像头捕获的人脸将要拿过来和这个特征值进行比对,进行人脸识别的处理;
源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
# 2018-5-11 # by timestamp # cnblogs: http://www.cnblogs.com/adaminxie # return_128d_features() 获取某张图像的128d特征 # write_into_csv() 将某个文件夹中的图像读取特征兵写入csv # compute_the_mean() 从csv中读取128d特征,并计算特征均值 import cv2 import os import dlib from skimage import io import csv import numpy as np import pandas as pd path_pics = "f:/code/python/p_dlib_face_reco/data/get_from_camera/" path_csv = "f:/code/python/p_dlib_face_reco/data/csvs/" # detector to find the faces detector = dlib.get_frontal_face_detector() # shape predictor to find the face landmarks predictor = dlib.shape_predictor( "shape_predictor_5_face_landmarks.dat" ) # face recognition model, the object maps human faces into 128d vectors facerec = dlib.face_recognition_model_v1( "dlib_face_recognition_resnet_model_v1.dat" ) # 返回单张图像的128d特征 def return_128d_features(path_img): img = io.imread(path_img) img_gray = cv2.cvtcolor(img, cv2.color_bgr2rgb) dets = detector(img_gray, 1 ) if ( len (dets)! = 0 ): shape = predictor(img_gray, dets[ 0 ]) face_descriptor = facerec.compute_face_descriptor(img_gray, shape) else : face_descriptor = 0 print ( "no face" ) # print(face_descriptor) return face_descriptor #return_128d_features(path_pics+"img_face_13.jpg") # 将文件夹中照片特征提取出来,写入csv # 输入input: # path_pics: 图像文件夹的路径 # path_csv: 要生成的csv路径 def write_into_csv(path_pics ,path_csv): dir_pics = os.listdir(path_pics) with open (path_csv, "w" , newline = "") as csvfile: writer = csv.writer(csvfile) for i in range ( len (dir_pics)): # 调用return_128d_features()得到128d特征 print (path_pics + dir_pics[i]) features_128d = return_128d_features(path_pics + dir_pics[i]) # print(features_128d) # 遇到没有检测出人脸的图片跳过 if features_128d = = 0 : i + = 1 else : writer.writerow(features_128d) #write_into_csv(path_pics, path_csv+"default_person.csv") path_csv_rd = "f:/code/python/p_dlib_face_reco/data/csvs/default_person.csv" # 从csv中读取数据,计算128d特征的均值 def compute_the_mean(path_csv_rd): column_names = [] for i in range ( 128 ): column_names.append( "features_" + str (i + 1 )) rd = pd.read_csv(path_csv_rd, names = column_names) # 存放128维特征的均值 feature_mean = [] for i in range ( 128 ): tmp_arr = rd[ "features_" + str (i + 1 )] tmp_arr = np.array(tmp_arr) # 计算某一个特征的均值 tmp_mean = np.mean(tmp_arr) feature_mean.append(tmp_mean) print (feature_mean) return feature_mean compute_the_mean(path_csv_rd) |
2.3 face_reco_from_camera.py / 实时人脸识别对比分析
这个py就是调用摄像头,捕获摄像头中的人脸,然后如果检测到人脸,将摄像头中的人脸提取出128d的特征,然后和预设的default_person的128d特征进行计算欧式距离,如果比较小,可以判定为一个人,否则不是一个人;
欧氏距离对比的阈值设定,是在 return_euclidean_distance函数的dist变量;
我这里程序里面指定的是0.4,具体阈值可以根据实际情况或者测得结果进行修改;
源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# 2018-5-11 # by timestamp # cnblogs: http://www.cnblogs.com/adaminxie import dlib # 人脸识别的库dlib import numpy as np # 数据处理的库numpy import cv2 # 图像处理的库opencv # face recognition model, the object maps human faces into 128d vectors facerec = dlib.face_recognition_model_v1( "dlib_face_recognition_resnet_model_v1.dat" ) # 计算两个向量间的欧式距离 def return_euclidean_distance(feature_1,feature_2): feature_1 = np.array(feature_1) feature_2 = np.array(feature_2) dist = np.sqrt(np. sum (np.square(feature_1 - feature_2))) print (dist) if dist > 0.4 : return "diff" else : return "same" features_mean_default_person = [ - 0.030892765492592986 , 0.13333227054068916 , 0.054221574805284799 , - 0.050820438289328626 , - 0.056331159841073189 , 0.0039378538311116004 , - 0.044465327145237675 , - 0.13096490031794497 , 0.14215188983239627 , - 0.084465635842398593 , 0.34389359700052363 , - 0.062936659118062566 , - 0.24372901571424385 , - 0.13270603316394905 , - 0.0472818422866495 , 0.15475224742763921 , - 0.24415240554433121 , - 0.11213862150907516 , 0.032288033417180964 , 0.023676671577911628 , 0.098508275653186594 , - 0.010117797634417289 , 0.0048202000815715448 , - 0.014808513420192819 , - 0.060100053486071135 , - 0.34934839135722112 , - 0.095795629448012301 , - 0.050788544706608117 , 0.032316677762489567 , - 0.099673464894294739 , - 0.080181991975558434 , 0.096361607705291952 , - 0.1823408101734362 , - 0.045472671817007815 , - 0.0066827326326778062 , 0.047393877549391041 , - 0.038414973079373964 , - 0.039067085930391363 , 0.15961966781239761 , 0.0092458106136243598 , - 0.16182226570029007 , 0.026322136191945327 , - 0.0039144184832510193 , 0.2492692768573761 , 0.19180528427425184 , 0.022950534855848866 , - 0.019220497949342979 , - 0.15331173021542399 , 0.047744840089427795 , - 0.17038608616904208 , 0.026140184680882254 , 0.19366614363695445 , 0.066497623724372762 , 0.07038829416820877 , - 0.0549700813073861 , - 0.11961311768544347 , - 0.032121153940495695 , 0.083507449611237169 , - 0.14934051350543373 , 0.011458799806668571 , 0.10686114273573223 , - 0.10744074888919529 , - 0.04377919611962218 , - 0.11030520381111848 , 0.20804878441910996 , 0.093076545941202266 , - 0.11621182490336268 , - 0.1991656830436305 , 0.10751579348978244 , - 0.11251544991606161 , - 0.12237925866716787 , 0.058218707869711672 , - 0.15829276019021085 , - 0.17670038891466042 , - 0.2718416170070046 , 0.034569320955166689 , 0.30443575821424784 , 0.061833358712886512 , - 0.19622498672259481 , 0.011373612000361868 , - 0.050225612756453063 , - 0.036157087079788507 , 0.12961127491373764 , 0.13962576616751521 , - 0.0074232793168017737 , 0.020964263007044792 , - 0.11185114399382942 , 0.012502493042694894 , 0.17834208513561048 , - 0.072658227462517586 , - 0.041312719401168194 , 0.25095899873658228 , - 0.056628625839948654 , 0.10285118379090961 , 0.046701753217923012 , 0.042323612264896691 , 0.0036216247826814651 , 0.066720707440062574 , - 0.16388990533979317 , - 0.0193739396421925 , 0.027835704435251261 , - 0.086023958105789985 , - 0.05472404568603164 , 0.14802298341926776 , - 0.10644183582381199 , 0.098863413851512108 , 0.00061285014778963834 , 0.062096107555063146 , 0.051960245755157973 , - 0.099548895108072383 , - 0.058173993112225285 , - 0.065454461562790375 , 0.14721672511414477 , - 0.25363486848379435 , 0.20384312381869868 , 0.16890435312923632 , 0.097537552447695477 , 0.087824966562421697 , 0.091438713434495431 , 0.093809676797766431 , - 0.034379941362299417 , - 0.085149037210564868 , - 0.24900743130006289 , 0.021165960517368819 , 0.076710369830068792 , - 0.0061752907196549996 , 0.028413473285342519 , - 0.029983982541843465 ] # dlib预测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor( 'shape_predictor_68_face_landmarks.dat' ) # 创建cv2摄像头对象 cap = cv2.videocapture( 0 ) # cap.set(propid, value) # 设置视频参数,propid设置的视频参数,value设置的参数值 cap. set ( 3 , 480 ) def get_128d_features(img_gray): dets = detector(img_gray, 1 ) if ( len (dets) ! = 0 ): shape = predictor(img_gray, dets[ 0 ]) face_descriptor = facerec.compute_face_descriptor(img_gray, shape) else : face_descriptor = 0 return face_descriptor # cap.isopened() 返回true/false 检查初始化是否成功 while (cap.isopened()): # cap.read() # 返回两个值: # 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾 # 图像对象,图像的三维矩阵 flag, im_rd = cap.read() # 每帧数据延时1ms,延时为0读取的是静态帧 kk = cv2.waitkey( 1 ) # 取灰度 img_gray = cv2.cvtcolor(im_rd, cv2.color_rgb2gray) # 人脸数rects rects = detector(img_gray, 0 ) # print(len(rects)) # 待会要写的字体 font = cv2.font_hershey_simplex cv2.puttext(im_rd, "q: quit" , ( 20 , 400 ), font, 0.8 , ( 0 , 255 , 255 ), 1 , cv2.line_aa) if ( len (rects) ! = 0 ): # 检测到人脸 # 将捕获到的人脸提取特征和内置特征进行比对 features_rd = get_128d_features(im_rd) compare = return_euclidean_distance(features_rd, features_mean_default_person) im_rd = cv2.puttext(im_rd, compare.replace( "same" , "default_person" ), ( 20 , 350 ), font, 0.8 , ( 0 , 255 , 255 ), 1 , cv2.line_aa) # 矩形框 for k, d in enumerate (rects): # 绘制矩形框 im_rd = cv2.rectangle(im_rd, tuple ([d.left(), d.top()]), tuple ([d.right(), d.bottom()]), ( 0 , 255 , 255 ), 2 ) cv2.puttext(im_rd, "faces: " + str ( len (rects)), ( 20 , 50 ), font, 1 , ( 0 , 0 , 255 ), 1 , cv2.line_aa) else : # 没有检测到人脸 cv2.puttext(im_rd, "no face" , ( 20 , 50 ), font, 1 , ( 0 , 0 , 255 ), 1 , cv2.line_aa) # 按下q键退出 if (kk = = ord ( 'q' )): break # 窗口显示 cv2.imshow( "camera" , im_rd) # 释放摄像头 cap.release() # 删除建立的窗口 cv2.destroyallwindows() |
实时输出结果:
图6 实时输出的欧氏距离结果
通过实时的输出结果,看的比较明显;
输出绿色部分:当是我自己(即之前分析提取特征的default_person)时,计算出来的欧式距离基本都在0.2 左右;
输出红色部分:而换一张图片上去比如特朗普,明显看到欧式距离计算结果达到了0.8,此时就可以判定,后来这张人脸不是我们预设的人脸;
所以之前提到的欧式距离计算对比的阈值可以由此设定,本项目中取的是0.4;
3.总结
之前接着那个摄像头人脸检测写的,不过拖到现在才更新,写的也比较粗糙,大家有具体需求和应用场景可以加以修改,有什么问题可以留言或者直接mail 我。。。不好意思
核心就是提取人脸特征,然后计算欧式距离和预设的特征脸进行比对;
不过这个实时获取摄像头人脸进行比对,要实时的进行计算摄像头脸的特征值,然后还要计算欧氏距离,所以计算量比较大,可能摄像头视频流会出现卡顿;
# 代码已上传到了我的github,如果对您有帮助欢迎star下:https://github.com/coneypo/dlib_face_recognition_from_camera
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.cnblogs.com/AdaminXie/p/9010298.html