• Python 3 利用 Dlib 实现摄像头实时人脸识别


    0. 引言

    利用 Python 开发,借助 Dlib 库捕获摄像头中的人脸,提取人脸特征,通过计算特征值之间的欧氏距离,来和预存的人脸特征进行对比,判断是否匹配,达到人脸识别的目的;

    可以从摄像头中抠取人脸图片存储到本地,然后提取构建预设人脸特征;

    根据抠取的 / 已有的同一个人多张人脸图片提取 128D 特征值,然后计算该人的 128D 特征均值;

    然后和摄像头中实时获取到的人脸提取出的特征值,计算欧氏距离,判定是否为同一张人脸;  

    Python + OpenCV + Dlib ; 

    识别模型:基于 Dlib 的 ResNet 预训练模型(dlib_face_recognition_resnet_model_v1.dat)

    识别算法:ResNet 神经网络(This model is a ResNet network with 29 conv layers. It's essentially a version of the ResNet-34 network from the paper Deep Residual Learning for Image Recognition by He, Zhang, Ren, and Sun with a few layers removed and the number of filters per layer reduced by half)

    1. 人脸检测

    faces = detector(img_gray, 0) -> <class 'dlib.dlib.rectangles'> -> 

    2. 计算特征点

    shape = predictor(img_rd, faces[i]) -> <class 'dlib.dlib.full_object_detection'> -> 

    3. 特征描述子

    facerec.compute_face_descriptor(img_rd, shape) -> <class 'dlib.dlib.vector'>

    博客中代码以 GitHub 为准,博客中可能没有及时更新;

      # Blog :    http://www.cnblogs.com/AdaminXie
      # GitHub :   https://github.com/coneypo/Dlib_face_recognition_from_camera

    Features :

    • 支持人脸数据采集,自行建立人脸数据库 / Support face register
    • 调用摄像头实时人脸检测和识别 / Using camera to real-time detect and recognize faces
    • 支持多张人脸 / Support multi-faces

    人脸识别 / Face Recognition 的说明:

    Wikipedia 上关于人脸识别系统 / Face Recognition System 的描述:they work by comparing selected facial features from given image with faces within a database.

    本项目中就是比较 预设的人脸的特征 摄像头实时获取到的人脸的特征 

    核心就是 提取 128D 人脸特征,然后计算 摄像头人脸特征 和 预设的特征脸的欧式距离,进行比对;

     

    效果如下:

       

    图 1 摄像头多个人脸时识别效果 

     

    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” 这个 pre-trained model,提取人脸图像的 128D 特征,然后比对不同人脸图片的 128D 特征的欧式距离,设定一个 Threshold / 阈值 来判断是否为同一张脸;

    1 # face recognition model, the object maps human faces into 128D vectors
    2 facerec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
    3 
    4 shape = predictor(img, dets[0])
    5 face_descriptor = facerec.compute_face_descriptor(img, shape)

     

       

     

    图 2 总体设计流程

     

    2.源码介绍

    主要有

    • get_faces_from_camera.py 
    • features_extraction_to_csv.py
    • face_reco_from_camera.py

    这三个 Python 文件,接下来会分别介绍实现功能;

     

    2.1 get_faces_from_camera.py / 人脸注册录入

    人脸识别需要将 提取到的图像数据 和 已有图像数据 进行比对分析,所以这部分代码实现的功能就是 人脸录入

    程序会生成一个窗口,显示调用的摄像头实时获取的图像;

    (关于摄像头的调用方式可以参考这里: Python 3 利用 Dlib 19.7 实现摄像头人脸检测特征点标定);

      

    然后根据键盘输入进行人脸捕获:

    • “N” 新录入人脸,新建文件夹 person_X/  用来存储某人的人脸图像
    •   "S" 开始捕获人脸,将捕获到的人脸放到 person_X/ 路径下
    • “Q” 退出窗口

      

    摄像头的调用是利用 opencv 库的 cv2.VideoCapture(0), 此处参数为 0 代表调用的是笔记本的默认摄像头,你也可以让它调用传入已有视频文件;

    可以参考 https://github.com/coneypo/Dlib_face_recognition_from_camera/blob/master/how_to_use_camera.py 如何通过 OpenCV 调用摄像头; 

     

    图 3  get_face_from_camera.py 的界面

       

    “N”+“S”之后捕获到的一组人脸示例;

    图 4 捕获到的一组人脸

     

    get_faces_from_camera.py 源码:

      1 # 进行人脸录入 / face register
      2 # 录入多张人脸 / support multi-faces
      3 
      4 # Author:   coneypo
      5 # Blog:     http://www.cnblogs.com/AdaminXie
      6 # GitHub:   https://github.com/coneypo/Dlib_face_recognition_from_camera
      7 # Mail:     coneypo@foxmail.com
      8 
      9 # Created at 2018-05-11
     10 # Updated at 2020-04-02
     11 
     12 import dlib         # 人脸处理的库 Dlib
     13 import numpy as np  # 数据处理的库 Numpy
     14 import cv2          # 图像处理的库 OpenCv
     15 import os           # 读写文件
     16 import shutil       # 读写文件
     17 
     18 # Dlib 正向人脸检测器 / frontal face detector
     19 detector = dlib.get_frontal_face_detector()
     20 
     21 # OpenCv 调用摄像头 / Use camera
     22 cap = cv2.VideoCapture(0)
     23 
     24 # 人脸截图的计数器 / The counter for screen shoot
     25 cnt_ss = 0
     26 
     27 # 存储人脸的文件夹 / The folder to save face images
     28 current_face_dir = ""
     29 
     30 # 保存 faces images 的路径 / The directory to save images of faces
     31 path_photos_from_camera = "data/data_faces_from_camera/"
     32 
     33 
     34 # 1. 新建保存人脸图像文件和数据CSV文件夹
     35 # 1. Mkdir for saving photos and csv
     36 def pre_work_mkdir():
     37 
     38     # 新建文件夹 / make folders to save faces images and csv
     39     if os.path.isdir(path_photos_from_camera):
     40         pass
     41     else:
     42         os.mkdir(path_photos_from_camera)
     43 
     44 
     45 pre_work_mkdir()
     46 
     47 
     48 ##### optional/可选, 默认关闭 #####
     49 # 2. 删除之前存的人脸数据文件夹
     50 # 2. Delete the old data of faces
     51 def pre_work_del_old_face_folders():
     52     # 删除之前存的人脸数据文件夹
     53     # 删除 "/data_faces_from_camera/person_x/"...
     54     folders_rd = os.listdir(path_photos_from_camera)
     55     for i in range(len(folders_rd)):
     56         shutil.rmtree(path_photos_from_camera+folders_rd[i])
     57 
     58     if os.path.isfile("data/features_all.csv"):
     59         os.remove("data/features_all.csv")
     60 
     61 # 这里在每次程序录入之前, 删掉之前存的人脸数据
     62 # 如果这里打开,每次进行人脸录入的时候都会删掉之前的人脸图像文件夹 person_1/,person_2/,person_3/...
     63 # If enable this function, it will delete all the old data in dir person_1/,person_2/,/person_3/...
     64 # pre_work_del_old_face_folders()
     65 ##################################
     66 
     67 
     68 # 3. Check people order: person_cnt
     69 # 如果有之前录入的人脸 / If the old folders exists
     70 # 在之前 person_x 的序号按照 person_x+1 开始录入 / Start from person_x+1
     71 if os.listdir("data/data_faces_from_camera/"):
     72     # 获取已录入的最后一个人脸序号 / Get the num of latest person
     73     person_list = os.listdir("data/data_faces_from_camera/")
     74     person_num_list = []
     75     for person in person_list:
     76         person_num_list.append(int(person.split('_')[-1]))
     77     person_cnt = max(person_num_list)
     78 
     79 # 如果第一次存储或者没有之前录入的人脸, 按照 person_1 开始录入
     80 # Start from person_1
     81 else:
     82     person_cnt = 0
     83 
     84 # 之后用来控制是否保存图像的 flag / The flag to control if save
     85 save_flag = 1
     86 
     87 # 之后用来检查是否先按 'n' 再按 's' / The flag to check if press 'n' before 's'
     88 press_n_flag = 0
     89 
     90 while cap.isOpened():
     91     flag, img_rd = cap.read()
     92     # print(img_rd.shape)
     93     # It should be 480 height * 640 width in Windows and Ubuntu by default
     94     # Maybe 1280x720 in macOS
     95 
     96     kk = cv2.waitKey(1)
     97 
     98     # 人脸 / Faces
     99     faces = detector(img_rd, 0)
    100 
    101     # 待会要写的字体 / Font to write
    102     font = cv2.FONT_ITALIC
    103 
    104     # 4. 按下 'n' 新建存储人脸的文件夹 / press 'n' to create the folders for saving faces
    105     if kk == ord('n'):
    106         person_cnt += 1
    107         current_face_dir = path_photos_from_camera + "person_" + str(person_cnt)
    108         os.makedirs(current_face_dir)
    109         print('
    ')
    110         print("新建的人脸文件夹 / Create folders: ", current_face_dir)
    111 
    112         cnt_ss = 0              # 将人脸计数器清零 / clear the cnt of faces
    113         press_n_flag = 1        # 已经按下 'n' / have pressed 'n'
    114 
    115     # 检测到人脸 / Face detected
    116     if len(faces) != 0:
    117         # 矩形框 / Show the rectangle box of face
    118         for k, d in enumerate(faces):
    119             # 计算矩形大小
    120             # Compute the width and height of the box
    121             # (x,y), (宽度width, 高度height)
    122             pos_start = tuple([d.left(), d.top()])
    123             pos_end = tuple([d.right(), d.bottom()])
    124 
    125             # 计算矩形框大小 / compute the size of rectangle box
    126             height = (d.bottom() - d.top())
    127             width = (d.right() - d.left())
    128 
    129             hh = int(height/2)
    130             ww = int(width/2)
    131 
    132             # 设置颜色 / the color of rectangle of faces detected
    133             color_rectangle = (255, 255, 255)
    134 
    135             # 判断人脸矩形框是否超出 480x640
    136             if (d.right()+ww) > 640 or (d.bottom()+hh > 480) or (d.left()-ww < 0) or (d.top()-hh < 0):
    137                 cv2.putText(img_rd, "OUT OF RANGE", (20, 300), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
    138                 color_rectangle = (0, 0, 255)
    139                 save_flag = 0
    140                 if kk == ord('s'):
    141                     print("请调整位置 / Please adjust your position")
    142             else:
    143                 color_rectangle = (255, 255, 255)
    144                 save_flag = 1
    145 
    146             cv2.rectangle(img_rd,
    147                           tuple([d.left() - ww, d.top() - hh]),
    148                           tuple([d.right() + ww, d.bottom() + hh]),
    149                           color_rectangle, 2)
    150 
    151             # 根据人脸大小生成空的图像 / Create blank image according to the shape of face detected
    152             img_blank = np.zeros((int(height*2), width*2, 3), np.uint8)
    153 
    154             if save_flag:
    155                 # 5. 按下 's' 保存摄像头中的人脸到本地 / Press 's' to save faces into local images
    156                 if kk == ord('s'):
    157                     # 检查有没有先按'n'新建文件夹 / check if you have pressed 'n'
    158                     if press_n_flag:
    159                         cnt_ss += 1
    160                         for ii in range(height*2):
    161                             for jj in range(width*2):
    162                                 img_blank[ii][jj] = img_rd[d.top()-hh + ii][d.left()-ww + jj]
    163                         cv2.imwrite(current_face_dir + "/img_face_" + str(cnt_ss) + ".jpg", img_blank)
    164                         print("写入本地 / Save into:", str(current_face_dir) + "/img_face_" + str(cnt_ss) + ".jpg")
    165                     else:
    166                         print("请在按 'S' 之前先按 'N' 来建文件夹 / Please press 'N' before 'S'")
    167 
    168     # 显示人脸数 / Show the numbers of faces detected
    169     cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
    170 
    171     # 添加说明 / Add some statements
    172     cv2.putText(img_rd, "Face Register", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA)
    173     cv2.putText(img_rd, "N: Create face folder", (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
    174     cv2.putText(img_rd, "S: Save current face", (20, 400), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
    175     cv2.putText(img_rd, "Q: Quit", (20, 450), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)
    176 
    177     # 6. 按下 'q' 键退出 / Press 'q' to exit
    178     if kk == ord('q'):
    179         break
    180 
    181     # 如果需要摄像头窗口大小可调 / Uncomment this line if you want the camera window is resizeable
    182     # cv2.namedWindow("camera", 0)
    183 
    184     cv2.imshow("camera", img_rd)
    185 
    186 # 释放摄像头 / Release camera and destroy all windows
    187 cap.release()
    188 cv2.destroyAllWindows()

    考虑到有可能需要保存的矩形框超出摄像头范围,对于这种异常,如果矩形框超出范围,矩形框会从白变红,然后提示 "OUT OF RANGE";

     

    图 5 人脸录入异常(Out of range)处理

    get_face_from_camera.py 的输出 log

    新建的人脸文件夹 / Create folders:  data/data_faces_from_camera/person_1
    写入本地 / Save into: data/data_faces_from_camera/person_1/img_face_1.jpg
    写入本地 / Save into: data/data_faces_from_camera/person_1/img_face_2.jpg
    写入本地 / Save into: data/data_faces_from_camera/person_1/img_face_3.jpg
    写入本地 / Save into: data/data_faces_from_camera/person_1/img_face_4.jpg
    
    
    新建的人脸文件夹 / Create folders:  data/data_faces_from_camera/person_2
    写入本地 / Save into: data/data_faces_from_camera/person_2/img_face_1.jpg
    写入本地 / Save into: data/data_faces_from_camera/person_2/img_face_2.jpg
    
    
    新建的人脸文件夹 / Create folders:  data/data_faces_from_camera/person_3
    写入本地 / Save into: data/data_faces_from_camera/person_3/img_face_1.jpg
    写入本地 / Save into: data/data_faces_from_camera/person_3/img_face_2.jpg

    2.2 features_extraction_to_csv.py / 将图像文件中人脸数据提取出来存入 CSV

    这部分代码实现的功能是将之前捕获到的人脸图像文件,提取出 128D 特征,然后计算出某人人脸数据的特征均值存入 CSV 中,方便之后识别时候进行比对;

    利用 numpy.mean() 计算特征均值,生成一个存储所有录入人脸数据 database 的 "features_all.csv";

    features_extraction_to_csv.py 源码:

     1 # 从人脸图像文件中提取人脸特征存入 CSV
     2 # Features extraction from images and save into features_all.csv
     3 
     4 # Author:   coneypo
     5 # Blog:     http://www.cnblogs.com/AdaminXie
     6 # GitHub:   https://github.com/coneypo/Dlib_face_recognition_from_camera
     7 # Mail:     coneypo@foxmail.com
     8 
     9 # Created at 2018-05-11
    10 # Updated at 2020-04-02
    11 
    12 import cv2
    13 import os
    14 import dlib
    15 from skimage import io
    16 import csv
    17 import numpy as np
    18 
    19 # 要读取人脸图像文件的路径
    20 path_images_from_camera = "data/data_faces_from_camera/"
    21 
    22 # Dlib 正向人脸检测器
    23 detector = dlib.get_frontal_face_detector()
    24 
    25 # Dlib 人脸预测器
    26 predictor = dlib.shape_predictor("data/data_dlib/shape_predictor_68_face_landmarks.dat")
    27 
    28 # Dlib 人脸识别模型
    29 # Face recognition model, the object maps human faces into 128D vectors
    30 face_rec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
    31 
    32 
    33 # 返回单张图像的 128D 特征
    34 def return_128d_features(path_img):
    35     img_rd = io.imread(path_img)
    36     faces = detector(img_rd, 1)
    37 
    38     print("%-40s %-20s" % ("检测到人脸的图像 / image with faces detected:", path_img), '
    ')
    39 
    40     # 因为有可能截下来的人脸再去检测,检测不出来人脸了
    41     # 所以要确保是 检测到人脸的人脸图像 拿去算特征
    42     if len(faces) != 0:
    43         shape = predictor(img_rd, faces[0])
    44         face_descriptor = face_rec.compute_face_descriptor(img_rd, shape)
    45     else:
    46         face_descriptor = 0
    47         print("no face")
    48 
    49     return face_descriptor
    50 
    51 
    52 # 将文件夹中照片特征提取出来, 写入 CSV
    53 def return_features_mean_personX(path_faces_personX):
    54     features_list_personX = []
    55     photos_list = os.listdir(path_faces_personX)
    56     if photos_list:
    57         for i in range(len(photos_list)):
    58             # 调用return_128d_features()得到128d特征
    59             print("%-40s %-20s" % ("正在读的人脸图像 / image to read:", path_faces_personX + "/" + photos_list[i]))
    60             features_128d = return_128d_features(path_faces_personX + "/" + photos_list[i])
    61             #  print(features_128d)
    62             # 遇到没有检测出人脸的图片跳过
    63             if features_128d == 0:
    64                 i += 1
    65             else:
    66                 features_list_personX.append(features_128d)
    67     else:
    68         print("文件夹内图像文件为空 / Warning: No images in " + path_faces_personX + '/', '
    ')
    69 
    70     # 计算 128D 特征的均值
    71     # personX 的 N 张图像 x 128D -> 1 x 128D
    72     if features_list_personX:
    73         features_mean_personX = np.array(features_list_personX).mean(axis=0)
    74     else:
    75         features_mean_personX = '0'
    76 
    77     return features_mean_personX
    78 
    79 
    80 # 获取已录入的最后一个人脸序号 / get the num of latest person
    81 person_list = os.listdir("data/data_faces_from_camera/")
    82 person_num_list = []
    83 for person in person_list:
    84     person_num_list.append(int(person.split('_')[-1]))
    85 person_cnt = max(person_num_list)
    86 
    87 with open("data/features_all.csv", "w", newline="") as csvfile:
    88     writer = csv.writer(csvfile)
    89     for person in range(person_cnt):
    90         # Get the mean/average features of face/personX, it will be a list with a length of 128D
    91         print(path_images_from_camera + "person_"+str(person+1))
    92         features_mean_personX = return_features_mean_personX(path_images_from_camera + "person_"+str(person+1))
    93         writer.writerow(features_mean_personX)
    94         print("特征均值 / The mean of features:", list(features_mean_personX))
    95         print('
    ')
    96     print("所有录入人脸数据存入 / Save all the features of faces registered into: data/features_all.csv")

    我们可以看下对于某张图片,face_descriptor 这个 128D vectors 的输出结果:

    绿色框内是我们的返回 128D 特征的函数;

    在红色框内调用该函数来计算 img_face_13.jpg;

    可以看到黄色框中的输出为 128D 的向量

    图 6 返回单张图像的 128D 特征的计算结果

     

    之后就需要人脸图像进行批量化操作,提取出 128D 的特征,然后计算特征均值,存入 features_all.csv;

    features_all.csv 是一个 n 行 128 列的 CSV, n 是录入的人脸数,128 列是某人的 128D 特征;

    这存储的就是 录入的人脸数据,之后 摄像头捕获的人脸 将要拿过来和 这些特征值 进行比对,如果欧式距离比较近的话,就可以认为是同一张人脸

     

     get_features_into_CSV.py 的输出 log:

    ##### person_1 #####
    data/data_csvs_from_camera/person_1.csv
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_1/img_face_1.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_1/img_face_1.jpg 
    
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_1/img_face_2.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_1/img_face_2.jpg 
    
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_1/img_face_3.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_1/img_face_3.jpg 
    
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_1/img_face_4.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_1/img_face_4.jpg 
    
    ##### person_2 #####
    data/data_csvs_from_camera/person_2.csv
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_2/img_face_1.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_2/img_face_1.jpg 
    
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_2/img_face_2.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_2/img_face_2.jpg 
    
    ##### person_3 #####
    data/data_csvs_from_camera/person_3.csv
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_3/img_face_1.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_3/img_face_1.jpg 
    
    正在读的人脸图像 / image to read:                data/data_faces_from_camera/person_3/img_face_2.jpg
    检测到人脸的图像 / image with faces detected:    data/data_faces_from_camera/person_3/img_face_2.jpg 
    
    
    ...

    2.3 face_reco_from_camera.py / 实时人脸识别对比分析

    这部分源码实现的功能:调用摄像头,捕获摄像头中的人脸,然后如果检测到人脸,将 摄像头中的人脸提取出 128D 的特征,然后和 之前录入人脸的 128D 特征 进行计算欧式距离,如果比较小,可以判定为一个人,否则不是一个人;

    伪代码如下:

    # 人脸检测器/预测器/识别模型
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
    facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
    
    faces = detector(img_gray, 0)
    
    # 如果检测到人脸
    if len(faces) != 0:
        # 遍历所有检测到的人脸
        for i in range(len(faces)):
            # 进行人脸比对
            shape = predictor(img_rd, faces[i])
            facerec.compute_face_descriptor(img_rd, shape)

     关于用到的 dlib 检测器,预测器,识别器:

    1. dlib.get_frontal_face_detector

    Link:

    http://dlib.net/python/index.html#dlib.get_frontal_face_detector

    简介 / intro:

    返回默认的人脸检测器,为下面的 fhog_object_detectorm / Returns the default face detector

     

    2. class dlib.fhog_object_detector

    Link:

    http://dlib.net/python/index.html#dlib.fhog_object_detector

    简介 / intro:

    基于滑动窗的HOG进行目标检测;

    This object represents a sliding window histogram-of-oriented-gradients based object detector.

    参数 / parameters:

    __call__(self: dlib.fhog_object_detector, image: array, upsample_num_times: int=0L) → dlib.rectangles

     

    3. class dlib.shape_predictor

    Link:

    http://dlib.net/python/index.html#dlib.shape_predictor

    简介 / intro:

    人脸图像作为输入, 输出面部特征点;

    This object is a tool that takes in an image region containing some object and outputs a set of point locations that define the pose of the object. 

    The classic example of this is human face pose prediction, where you take an image of a human face as input and are expected to identify

    the locations of important facial landmarks such as the corners of the mouth and eyes, tip of the nose, and so forth.

    参数 / parameters:

    __call__(self: dlib.shape_predictor, image: array, box: dlib.rectangle) → dlib.full_object_detection

    输入: dlib.rectangle 输出: dlib.full_object_detection

     

    4. class dlib.face_recognition_model_v1

    Link:

    http://dlib.net/python/index.html#dlib.face_recognition_model_v1

    简介 / intro:

    将人脸转换为128D特征向量, 这样的话相似人脸会比较相近, 不相像的会比较远;

    This object maps human faces into 128D vectors where pictures of the same person are mapped near to each other and pictures of different people are mapped far apart.

    The constructor loads the face recognition model from a file. The model file is available here: http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2

    参数 / parameters:

    compute_face_descriptor(self: dlib.face_recognition_model_v1, img: numpy.ndarray[(rows,cols,3),uint8], face: dlib.full_object_detection, num_jitters: int=0L, padding: float=0.25) -> dlib.vector

     

    通过 print(type()) 可以更清楚的看到 dlib 对象的传递:

    # 人脸检测器/预测器/识别模型
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
    facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
    
    faces = detector(img_gray, 0)
    
    # 如果检测到人脸
    if len(faces) != 0:
        print(type(faces)                                                # <class 'dlib.dlib.rectangles'>
        # 遍历所有检测到的人脸
        for i in range(len(faces)):
            # 进行人脸比对
            shape = predictor(img_rd, faces[i])
            print(type(shape))                                            # <class 'dlib.dlib.full_object_detection'>
            facerec.compute_face_descriptor(img_rd, shape)
            print(type(facerec.compute_face_descriptor(img_rd, shape))    # <class 'dlib.dlib.vector'>

    这样一个对象传递过程:

    faces = detector(img_gray, 0) -> <class 'dlib.dlib.rectangles'> -> 
    shape = predictor(img_rd, faces[i]) -> <class 'dlib.dlib.full_object_detection'> -> 
    facerec.compute_face_descriptor(img_rd, shape) -> <class 'dlib.dlib.vector'>

    欧氏距离对比的阈值设定,是在 return_euclidean_distance 函数的 dist  变量;

    我这里程序里面指定的 欧氏距离判断阈值是 0.4,具体阈值可以根据实际情况或者测得结果进行修改;

      

    这边做了一个,让人名跟随显示在头像下方,如果想要在人脸矩形框下方显示人名,首先需要知道 Dlib 生成的矩形框的尺寸怎么读取;

    Dlib 返回的 dets 变量是一系列人脸的数据,此处对单张人脸处理,所以取 dets[0] 的参数;

    可以通过 dets[0].top()dets[0].bottom()dets[0].left() 和 dets[0].right() 来确定要显示的人名的坐标;

    图 7 dets[0].top() 等参数说明 

      

    得到矩形框的坐标,就可以获取人名的相对位置;

    这是我这边取的坐标:

     pos_text_1 = tuple([dets[0].left(), int(dets[0].bottom()+(dets[0].bottom()-dets[0].top())/4)])

     

      

     

    图 8 face_reco_from_camera.py 生成的人脸识别窗口界面

       如果想定制输出显示的名字而不是“Person_1”,"Person_2"...;

    图 9 定制显示名字

    face_reco_from_camera.py 源码:

      1 # 摄像头实时人脸识别
      2 # Real-time face recognition
      3 
      4 # Author:   coneypo
      5 # Blog:     http://www.cnblogs.com/AdaminXie
      6 # GitHub:   https://github.com/coneypo/Dlib_face_recognition_from_camera
      7 
      8 # Created at 2018-05-11
      9 # Updated at 2020-04-02
     10 
     11 import dlib          # 人脸处理的库 Dlib
     12 import numpy as np   # 数据处理的库 numpy
     13 import cv2           # 图像处理的库 OpenCv
     14 import pandas as pd  # 数据处理的库 Pandas
     15 import os
     16 
     17 # 人脸识别模型,提取128D的特征矢量
     18 # face recognition model, the object maps human faces into 128D vectors
     19 # Refer this tutorial: http://dlib.net/python/index.html#dlib.face_recognition_model_v1
     20 facerec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat")
     21 
     22 
     23 # 计算两个128D向量间的欧式距离
     24 # Compute the e-distance between two 128D features
     25 def return_euclidean_distance(feature_1, feature_2):
     26     feature_1 = np.array(feature_1)
     27     feature_2 = np.array(feature_2)
     28     dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
     29     return dist
     30 
     31 
     32 # 1. Check 存放所有人脸特征的 csv
     33 if os.path.exists("data/features_all.csv"):
     34     path_features_known_csv = "data/features_all.csv"
     35     csv_rd = pd.read_csv(path_features_known_csv, header=None)
     36 
     37     # 用来存放所有录入人脸特征的数组
     38     # The array to save the features of faces in the database
     39     features_known_arr = []
     40 
     41     # 2. 读取已知人脸数据
     42     # Print known faces
     43     for i in range(csv_rd.shape[0]):
     44         features_someone_arr = []
     45         for j in range(0, len(csv_rd.iloc[i])):
     46             features_someone_arr.append(csv_rd.iloc[i][j])
     47         features_known_arr.append(features_someone_arr)
     48     print("Faces in Database:", len(features_known_arr))
     49 
     50     # Dlib 检测器和预测器
     51     # The detector and predictor will be used
     52     detector = dlib.get_frontal_face_detector()
     53     predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat')
     54 
     55     # 创建 cv2 摄像头对象
     56     cap = cv2.VideoCapture(0)
     57 
     58     # 3. When the camera is open
     59     while cap.isOpened():
     60 
     61         flag, img_rd = cap.read()
     62         faces = detector(img_rd, 0)
     63 
     64         # 待会要写的字体 font to write later
     65         font = cv2.FONT_ITALIC
     66 
     67         # 存储当前摄像头中捕获到的所有人脸的坐标/名字
     68         # The list to save the positions and names of current faces captured
     69         pos_namelist = []
     70         name_namelist = []
     71 
     72         kk = cv2.waitKey(1)
     73 
     74         # 按下 q 键退出
     75         # press 'q' to exit
     76         if kk == ord('q'):
     77             break
     78         else:
     79             # 检测到人脸 when face detected
     80             if len(faces) != 0:
     81                 # 4. 获取当前捕获到的图像的所有人脸的特征,存储到 features_cap_arr
     82                 # 4. Get the features captured and save into features_cap_arr
     83                 features_cap_arr = []
     84                 for i in range(len(faces)):
     85                     shape = predictor(img_rd, faces[i])
     86                     features_cap_arr.append(facerec.compute_face_descriptor(img_rd, shape))
     87 
     88                 # 5. 遍历捕获到的图像中所有的人脸
     89                 # 5. Traversal all the faces in the database
     90                 for k in range(len(faces)):
     91                     print("##### camera person", k+1, "#####")
     92                     # 让人名跟随在矩形框的下方
     93                     # 确定人名的位置坐标
     94                     # 先默认所有人不认识,是 unknown
     95                     # Set the default names of faces with "unknown"
     96                     name_namelist.append("unknown")
     97 
     98                     # 每个捕获人脸的名字坐标 the positions of faces captured
     99                     pos_namelist.append(tuple([faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top())/4)]))
    100 
    101                     # 对于某张人脸,遍历所有存储的人脸特征
    102                     # For every faces detected, compare the faces in the database
    103                     e_distance_list = []
    104                     for i in range(len(features_known_arr)):
    105                         # 如果 person_X 数据不为空
    106                         if str(features_known_arr[i][0]) != '0.0':
    107                             print("with person", str(i + 1), "the e distance: ", end='')
    108                             e_distance_tmp = return_euclidean_distance(features_cap_arr[k], features_known_arr[i])
    109                             print(e_distance_tmp)
    110                             e_distance_list.append(e_distance_tmp)
    111                         else:
    112                             # 空数据 person_X
    113                             e_distance_list.append(999999999)
    114                     # Find the one with minimum e distance
    115                     similar_person_num = e_distance_list.index(min(e_distance_list))
    116                     print("Minimum e distance with person", int(similar_person_num)+1)
    117 
    118                     if min(e_distance_list) < 0.4:
    119                         ####### 在这里修改 person_1, person_2 ... 的名字 ########
    120                         # 可以在这里改称 Jack, Tom and others
    121                         # Here you can modify the names shown on the camera
    122                         name_namelist[k] = "Person "+str(int(similar_person_num)+1)
    123                         print("May be person "+str(int(similar_person_num)+1))
    124                     else:
    125                         print("Unknown person")
    126 
    127                     # 矩形框
    128                     # draw rectangle
    129                     for kk, d in enumerate(faces):
    130                         # 绘制矩形框
    131                         cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2)
    132                     print('
    ')
    133 
    134                 # 6. 在人脸框下面写人脸名字
    135                 # 6. write names under rectangle
    136                 for i in range(len(faces)):
    137                     cv2.putText(img_rd, name_namelist[i], pos_namelist[i], font, 0.8, (0, 255, 255), 1, cv2.LINE_AA)
    138 
    139         print("Faces in camera now:", name_namelist, "
    ")
    140 
    141         cv2.putText(img_rd, "Press 'q': Quit", (20, 450), font, 0.8, (84, 255, 159), 1, cv2.LINE_AA)
    142         cv2.putText(img_rd, "Face Recognition", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA)
    143         cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
    144 
    145         cv2.imshow("camera", img_rd)
    146 
    147     cap.release()
    148     cv2.destroyAllWindows()
    149 
    150 else:
    151     print('##### Warning #####', '
    ')
    152     print("'features_all.py' not found!")
    153     print("Please run 'get_faces_from_camera.py' and 'features_extraction_to_csv.py' before 'face_reco_from_camera.py'", '
    ')
    154     print('##### Warning #####')

    face_reco_from_camera.py 输出 log:

    ##### camera person 1 #####
    with person 1 the e distance: 0.21153867687451736
    with person 2 the e distance: 0.20646924127167549
    with person 4 the e distance: 0.19824469336759548
    Minimum e distance with person 4
    May be person 4
    
    
    ##### camera person 2 #####
    with person 1 the e distance: 0.7403020289640347
    with person 2 the e distance: 0.7375521667680703
    with person 4 the e distance: 0.7077921161820342
    Minimum e distance with person 4
    Unknown person
    
    
    ##### camera person 3 #####
    with person 1 the e distance: 0.6975665799095466
    with person 2 the e distance: 0.7070867672498581
    with person 4 the e distance: 0.6727276688350984
    Minimum e distance with person 4
    Unknown person
    
    
    Faces in camera now: ['Person 4', 'unknown', 'unknown'] 

    如果对单个人脸,进行实时对比输出:

    图 10 实时输出的欧氏距离结果

     

      通过实时的输出结果,看的比较明显;

      输出绿色部分:当是我自己时,计算出来的欧式距离基本都在 0.2 左右

      输出红色部分:而换一张图片上去比如特朗普,明显看到欧式距离计算结果 达到了 0.8,此时就可以判定,后来这张人脸不是一张人脸;

      所以之前提到的欧式距离计算对比的阈值可以由此设定,本项目中取的是 dist=0.4;

       dist 的确切取值自己权衡,http://dlib.net/face_recognition.py.html 的说明:

    #   When using a distance threshold of 0.6, the dlib model obtains an accuracy
    #   of 99.38% on the standard LFW face recognition benchmark, which is
    #   comparable to other state-of-the-art methods for face recognition as of
    #   February 2017. This accuracy means that, when presented with a pair of face
    #   images, the tool will correctly identify if the pair belongs to the same
    #   person or is from different people 99.38% of the time.

    3. 总结

    核心就是 提取人脸特征,然后计算欧式距离和预设的特征脸进行比对;

    不过这个实时获取摄像头人脸进行比对,要实时的进行计算摄像头脸的特征值,然后还要计算欧氏距离,所以计算量比较大,可能摄像头视频流会出现卡顿;

    此项目仅个人学习爱好研究,开源供大家一起学习;

     

    # 请尊重他人劳动成果,转载或者使用源码请注明出处:http://www.cnblogs.com/AdaminXie

    # 代码已上传到了我的 GitHub,如果对您有帮助欢迎 Star 支持我下:https://github.com/coneypo/Dlib_face_recognition_from_camera

    # 如有问题请留言或者联系邮箱: coneypo@foxmail.com

    # Last update: 6 Apr

  • 相关阅读:
    Dom 动态添加元素节点总结
    SQLserver 获取当前时间
    Var的用法解析
    JS 转换HTML转义符
    20210602---为了养老,全力以赴,珍惜每一秒钟。决心不够大,不够担心未来,现在虽然挣得少,但是有吃有喝,满足了。
    20210601——今天开始狠狠奖励自己,而且是必须玩的这种。做事投入你就会快乐。
    20210531兴趣
    20210527学习笔记--没成功的唯一原因是,想得和说的太多 做的太少。
    20210526--今年还有半年,抓紧一切时间学习
    20210524学习笔记---从记日记开始已经有3个月了,浪费时间的痕迹渐渐清醒
  • 原文地址:https://www.cnblogs.com/AdaminXie/p/9010298.html
Copyright © 2020-2023  润新知