├── README.md ├── save_feature.py ├── face_reco.py └── get_face.py /README.md: -------------------------------------------------------------------------------- 1 | # Machine-Vision 2 | ## 人脸识别项目[参考](https://github.com/coneypo/Dlib_face_recognition_from_camera) 3 | - 新增限制已录入照片人员录入照片 4 | - 改进人脸名称文件夹支持手动中文输入 5 | - 支持手动输入人脸并且自动保存识别人脸 6 | - 改进人脸照片中文名称保存 7 | - 识别的时候[中文显示人名](https://github.com/huangzy97/Machine-Vision/blob/master/face_reco.py) 8 | ![image](https://github.com/huangzy97/lib/blob/master/%E4%B8%AD%E6%96%87%E6%98%BE%E7%A4%BA.gif) 9 | -------------------------------------------------------------------------------- /save_feature.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 5 09:25:26 2019 4 | 5 | @author: sbtithzy 6 | """ 7 | # 从人脸图像文件中提取人脸特征存入 CSV 8 | import cv2 9 | import os 10 | import dlib 11 | from skimage import io 12 | import csv 13 | import numpy as np 14 | # 要读取人脸图像文件的路径 15 | path_images_from_camera = "data/data_faces_from_camera/" 16 | # Dlib 正向人脸检测器 17 | detector = dlib.get_frontal_face_detector() 18 | # Dlib 人脸预测器 19 | predictor = dlib.shape_predictor("data/data_dlib/shape_predictor_5_face_landmarks.dat") 20 | # Dlib 人脸识别模型 21 | # Face recognition model, the object maps human faces into 128D vectors 22 | face_rec = dlib.face_recognition_model_v1("data/data_dlib/dlib_face_recognition_resnet_model_v1.dat") 23 | # 返回单张图像的 128D 特征 24 | def return_128d_features(path_img): 25 | img_rd = io.imread(path_img) 26 | img_gray = cv2.cvtColor(img_rd, cv2.COLOR_BGR2RGB) 27 | faces = detector(img_gray, 1) 28 | 29 | print("%-40s %-20s" % ("检测到人脸的图像 / image with faces detected:", path_img), '\n') 30 | 31 | # 因为有可能截下来的人脸再去检测,检测不出来人脸了 32 | # 所以要确保是 检测到人脸的人脸图像 拿去算特征 33 | if len(faces) != 0: 34 | shape = predictor(img_gray, faces[0]) 35 | face_descriptor = face_rec.compute_face_descriptor(img_gray, shape) 36 | else: 37 | face_descriptor = 0 38 | print("no face") 39 | return face_descriptor 40 | # 将文件夹中照片特征提取出来, 写入 CSV 41 | def return_features_mean_personX(path_faces_personX): 42 | features_list_personX = [] 43 | photos_list = os.listdir(path_faces_personX) 44 | if photos_list: 45 | for i in range(len(photos_list)): 46 | # 调用return_128d_features()得到128d特征 47 | print("%-40s %-20s" % ("正在读的人脸图像 / image to read:", path_faces_personX + "/" + photos_list[i])) 48 | features_128d = return_128d_features(path_faces_personX + "/" + photos_list[i]) 49 | # print(features_128d) 50 | # 遇到没有检测出人脸的图片跳过 51 | if features_128d == 0: 52 | i += 1 53 | else: 54 | features_list_personX.append(features_128d) 55 | else: 56 | print("文件夹内图像文件为空 / Warning: No images in " + path_faces_personX + '/', '\n') 57 | 58 | # 计算 128D 特征的均值 59 | # personX 的 N 张图像 x 128D -> 1 x 128D 60 | if features_list_personX: 61 | features_mean_personX = np.array(features_list_personX).mean(axis=0) 62 | else: 63 | features_mean_personX = '0' 64 | return features_mean_personX 65 | # 获取已录入的最后一个人脸序号 / get the num of latest person 66 | person_list = os.listdir("data/data_faces_from_camera/") 67 | person_num_list = [] 68 | for person in person_list: 69 | person_num_list.append(int(person.split('_')[0])) 70 | person_cnt = max(person_num_list) 71 | person_name_list = [] 72 | for person in person_list: 73 | #person_name_list.append(str(person.split('_')[0])) 74 | person_name_list.append(person) 75 | #person_cnt = max(person_num_list) 76 | 77 | with open("data/features_all.csv", "w", newline="") as csvfile: 78 | writer = csv.writer(csvfile) 79 | for person in range(person_cnt): 80 | # Get the mean/average features of face/personX, it will be a list with a length of 128D 81 | print(path_images_from_camera + str(person_name_list[person])) 82 | features_mean_personX = return_features_mean_personX(path_images_from_camera + str(person_name_list[person])) 83 | writer.writerow(features_mean_personX) 84 | print("特征均值 / The mean of features:", list(features_mean_personX)) 85 | print('\n') 86 | print("所有录入人脸数据存入 / Save all the features of faces registered into: data/features_all.csv") 87 | -------------------------------------------------------------------------------- /face_reco.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 8 13:55:57 2019 4 | 5 | @author: sbtithzy 6 | """ 7 | 8 | # -*- coding: utf-8 -*- 9 | """ 10 | Created on Fri Jul 5 10:07:33 2019 11 | @author: sbtithzy 12 | """ 13 | import dlib # 人脸处理的库 Dlib 14 | import numpy as np # 数据处理的库 numpy 15 | import cv2 # 图像处理的库 OpenCv 16 | import pandas as pd # 数据处理的库 Pandas 17 | import time 18 | # 人脸识别模型,提取128D的特征矢量 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 | # 计算两个128D向量间的欧式距离 22 | def return_euclidean_distance(feature_1, feature_2): 23 | feature_1 = np.array(feature_1) 24 | feature_2 = np.array(feature_2) 25 | dist = np.sqrt(np.sum(np.square(feature_1 - feature_2))) 26 | return dist 27 | # 处理存放所有人脸特征的 csv 28 | path_features_known_csv = "data/features_all.csv" 29 | csv_rd = pd.read_csv(path_features_known_csv, header=None) 30 | # 用来存放所有录入人脸特征的数组 31 | features_known_arr = [] 32 | # 读取已知人脸数据 33 | for i in range(csv_rd.shape[0]): 34 | features_someone_arr = [] 35 | for j in range(0, len(csv_rd.ix[i, :])): 36 | features_someone_arr.append(csv_rd.ix[i, :][j]) 37 | features_known_arr.append(features_someone_arr) 38 | print("Faces in Database:", len(features_known_arr)) 39 | # Dlib 检测器和预测器 40 | # The detector and predictor will be used 41 | detector = dlib.get_frontal_face_detector() 42 | predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat') 43 | # 创建 cv2 摄像头对象 44 | # cv2.VideoCapture(0) to use the default camera of PC, 45 | # and you can use local video name by use cv2.VideoCapture(filename) 46 | cap = cv2.VideoCapture(0) 47 | # 设置视频参数,propId 设置的视频参数,value 设置的参数值 48 | cap.set(3, 480) 49 | # cap.isOpened() 返回 true/false 检查初始化是否成功 50 | # when the camera is open 51 | # 中文显示名称 52 | import cv2 53 | import numpy 54 | from PIL import Image, ImageDraw, ImageFont 55 | def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20): 56 | if (isinstance(img, numpy.ndarray)): #判断是否OpenCV图片类型 57 | img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) 58 | draw = ImageDraw.Draw(img) 59 | fontText = ImageFont.truetype( 60 | "font/simsun.ttc", textSize, encoding="utf-8") 61 | draw.text((left, top), text, textColor, font=fontText) 62 | return cv2.cvtColor(numpy.asarray(img), cv2.COLOR_RGB2BGR) 63 | while cap.isOpened(): 64 | flag, img_rd = cap.read() 65 | kk = cv2.waitKey(1) 66 | # 取灰度 67 | img_gray = cv2.cvtColor(img_rd, cv2.COLOR_RGB2GRAY) 68 | # 人脸数 faces 69 | faces = detector(img_gray, 0) 70 | # 待会要写的字体 font to write later 71 | font = cv2.FONT_HERSHEY_COMPLEX 72 | # 存储当前摄像头中捕获到的所有人脸的坐标/名字 73 | # the list to save the positions and names of current faces captured 74 | pos_namelist = [] 75 | name_namelist = [] 76 | # 按下 q 键退出 77 | # press 'q' to exit 78 | if kk == ord('q'): 79 | break 80 | else: 81 | # 检测到人脸 when face detected 82 | if len(faces) != 0: 83 | # 获取当前捕获到的图像的所有人脸的特征,存储到 features_cap_arr 84 | # get the features captured and save into features_cap_arr 85 | features_cap_arr = [] 86 | for i in range(len(faces)): 87 | shape = predictor(img_rd, faces[i]) 88 | features_cap_arr.append(facerec.compute_face_descriptor(img_rd, shape)) 89 | # 遍历捕获到的图像中所有的人脸 90 | # traversal all the faces in the database 91 | for k in range(len(faces)): 92 | print("##### camera person", k+1, "#####") 93 | # 让人名跟随在矩形框的下方 94 | # 确定人名的位置坐标 95 | # 先默认所有人不认识,是 unknown 96 | name_namelist.append("unknown") 97 | # 每个捕获人脸的名字坐标 the positions of faces captured 98 | pos_namelist.append(tuple([faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top())/4)])) 99 | # 对于某张人脸,遍历所有存储的人脸特征 100 | e_distance_list = [] 101 | for i in range(len(features_known_arr)): 102 | # 如果 person_X 数据不为空 103 | if str(features_known_arr[i][0]) != '0.0': 104 | print("with person", str(i + 1), "the e distance: ", end='') 105 | e_distance_tmp = return_euclidean_distance(features_cap_arr[k], features_known_arr[i]) 106 | print(e_distance_tmp) 107 | e_distance_list.append(e_distance_tmp) 108 | else: 109 | # 空数据 person_X 110 | e_distance_list.append(999999999) 111 | # Find the one with minimum e distance 112 | similar_person_num = e_distance_list.index(min(e_distance_list)) 113 | print("Minimum e distance with person", int(similar_person_num)+1) 114 | if min(e_distance_list) < 0.4: 115 | name_namelist[k] = str("Person "+str(int(similar_person_num)+1))\ 116 | .replace("Person 1", "中文")\###这里可以考虑做字典的映射,待改进的地方 117 | .replace("Person 2", "小明")\ 118 | .replace("Person 3", "Ronnie")\ 119 | .replace("Person 4", "Terry")\ 120 | .replace("Person 5", "Wilson") 121 | print("May be person "+str(int(similar_person_num)+1)) 122 | else: 123 | print("Unknown person") 124 | # 矩形框 125 | for kk, d in enumerate(faces): 126 | # 绘制矩形框 127 | cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2) 128 | print('\n') 129 | # 在人脸框下面写人脸名字 130 | for i in range(len(faces)): 131 | img_rd = cv2ImgAddText(img_rd, name_namelist[i], pos_namelist[i][0], pos_namelist[i][1], (0, 255, 255), 50) 132 | img_rd = cv2ImgAddText(img_rd, "按'Q':退出", 20, 430, (84, 255, 159), 30) 133 | cv2.putText(img_rd, "Face Recognition", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA) 134 | img_rd = cv2ImgAddText(img_rd, "人脸:" + str(len(faces)), 20, 80, (0, 0, 255), 30) 135 | # 窗口显示 show with opencv 136 | cv2.imshow("camera", img_rd) 137 | # 释放摄像头 release camera 138 | cap.release() 139 | # 删除建立的窗口 delete all the windows 140 | cv2.destroyAllWindows() 141 | -------------------------------------------------------------------------------- /get_face.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 4 20:42:54 2019 4 | 5 | @author: sbtithzy 6 | """ 7 | import dlib # 人脸处理的库 Dlib 8 | import numpy as np # 数据处理的库 Numpy 9 | import cv2 # 图像处理的库 OpenCv 10 | import os # 读写文件 11 | import shutil # 读写文件 12 | # Dlib 正向人脸检测器 / frontal face detector 13 | detector = dlib.get_frontal_face_detector() 14 | #detector = dlib.cnn_face_detection_model_v1() 15 | # Dlib 68 点特征预测器 / 68 points features predictor 16 | predictor = dlib.shape_predictor('data/data_dlib/shape_predictor_68_face_landmarks.dat') 17 | # OpenCv 调用摄像头 use camera 18 | cap = cv2.VideoCapture(0) 19 | # 设置视频参数 set camera 20 | cap.set(3, 480) 21 | # 人脸截图的计数器 the counter for screen shoot 22 | cnt_ss = 0 23 | # 存储人脸的文件夹 the folder to save faces 24 | current_face_dir = "" 25 | # 保存 faces images 的路径 the directory to save images of faces 26 | path_photos_from_camera = "data/data_faces_from_camera/" 27 | # 新建保存人脸图像文件和数据CSV文件夹 28 | # mkdir for saving photos and csv 29 | def pre_work_mkdir(): 30 | # 新建文件夹 / make folders to save faces images and csv 31 | if os.path.isdir(path_photos_from_camera): 32 | pass 33 | else: 34 | os.mkdir(path_photos_from_camera) 35 | pre_work_mkdir() 36 | ##### optional/可选, 默认关闭 ##### 37 | # 删除之前存的人脸数据文件夹 38 | # delete the old data of faces 39 | def pre_work_del_old_face_folders(): 40 | # 删除之前存的人脸数据文件夹 41 | # 删除 "/data_faces_from_camera/person_x/"... 42 | folders_rd = os.listdir(path_photos_from_camera) 43 | for i in range(len(folders_rd)): 44 | shutil.rmtree(path_photos_from_camera+folders_rd[i]) 45 | if os.path.isfile("data/features_all.csv"): 46 | os.remove("data/features_all.csv") 47 | if os.listdir("data/data_faces_from_camera/"): 48 | # 获取已录入的最后一个人脸序号 / get the num of latest person 49 | person_list = os.listdir("data/data_faces_from_camera/") 50 | person_num_list = [] 51 | for person in person_list: 52 | person_num_list.append(int(person.split('_')[-1])) 53 | person_cnt = max(person_num_list) 54 | # 如果第一次存储或者没有之前录入的人脸, 按照 person_1 开始录入 55 | # start from person_1 56 | else: 57 | person_cnt = 0 58 | # 之后用来控制是否保存图像的 flag / the flag to control if save 59 | save_flag = 1 60 | # 之后用来检查是否先按 'n' 再按 's' / the flag to check if press 'n' before 's' 61 | press_n_flag = 0 62 | while cap.isOpened(): 63 | flag, img_rd = cap.read() 64 | kk = cv2.waitKey(1) 65 | img_gray = cv2.cvtColor(img_rd, cv2.COLOR_RGB2GRAY) 66 | # 人脸数 faces 67 | faces = detector(img_gray, 0) 68 | # 待会要写的字体 / font to write 69 | font = cv2.FONT_HERSHEY_COMPLEX 70 | # 按下 'n' 新建存储人脸的文件夹 / press 'n' to create the folders for saving faces 71 | if kk == ord('n'): 72 | person_cnt += 1 73 | name = input("Enter your name: ")##支持中文人名文件夹名称的手工录入 74 | person_list = os.listdir("data/data_faces_from_camera/") 75 | person_name = [] 76 | for person in person_list: 77 | person_name.append(str(person.split('_')[0])) 78 | if name in person_name:###判断录入人的照片是否存在,如果已经录入无需重新录入 79 | print ("用户照片已存在,无需重复录入!!!") 80 | print("请重新键盘输入'N'来新建人脸文件夹!") 81 | person_cnt -= 1 82 | continue 83 | else: 84 | current_face_dir = path_photos_from_camera + name + '_'+ str(person_cnt) 85 | os.makedirs(current_face_dir) 86 | print('\n') 87 | print("新建的人脸文件夹: ", current_face_dir) 88 | print("键盘输入'S'保存照片") 89 | cnt_ss = 0 # 将人脸计数器清零 / clear the cnt of faces 90 | press_n_flag = 1 # 已经按下 'n' / have pressed 'n' 91 | # 检测到人脸 / if face detected 92 | if len(faces) != 0: 93 | # 矩形框 / show the rectangle box 94 | for k, d in enumerate(faces): 95 | # 计算矩形大小 96 | # we need to compute the width and height of the box 97 | # (x,y), (宽度width, 高度height) 98 | pos_start = tuple([d.left(), d.top()]) 99 | pos_end = tuple([d.right(), d.bottom()]) 100 | # 计算矩形框大小 / compute the size of rectangle box 101 | height = (d.bottom() - d.top()) 102 | width = (d.right() - d.left()) 103 | hh = int(height/2) 104 | ww = int(width/2) 105 | # 设置颜色 / the color of rectangle of faces detected 106 | color_rectangle = (255, 255, 255) 107 | # 判断人脸矩形框是否超出 480x640 108 | if (d.right()+ww) > 640 or (d.bottom()+hh > 480) or (d.left()-ww < 0) or (d.top()-hh < 0): 109 | cv2.putText(img_rd, "OUT OF RANGE", (20, 300), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA) 110 | color_rectangle = (0, 0, 255) 111 | save_flag = 0 112 | if kk == ord('s'): 113 | print("请调整位置 / Please adjust your position") 114 | else: 115 | color_rectangle = (255, 255, 255) 116 | save_flag = 1 117 | cv2.rectangle(img_rd, 118 | tuple([d.left() - ww, d.top() - hh]), 119 | tuple([d.right() + ww, d.bottom() + hh]), 120 | color_rectangle, 2) 121 | # 根据人脸大小生成空的图像 / create blank image according to the size of face detected 122 | im_blank = np.zeros((int(height*2), width*2, 3), np.uint8) 123 | if save_flag: 124 | # 按下 's' 保存摄像头中的人脸到本地 / press 's' to save faces into local images 125 | if kk == ord('s'): 126 | # 检查有没有先按'n'新建文件夹 / check if you have pressed 'n' 127 | if press_n_flag: 128 | cnt_ss += 1 129 | for ii in range(height*2): 130 | for jj in range(width*2): 131 | im_blank[ii][jj] = img_rd[d.top()-hh + ii][d.left()-ww + jj] 132 | #cv2.imwrite(current_face_dir + "/img_face_" + str(cnt_ss) + ".jpg", im_blank)##原方法 133 | cv2.imencode('.jpg', im_blank)[1].tofile(current_face_dir + "/" + name +'_'+ str(cnt_ss)+ ".jpg") #中文名称照片保存 134 | 135 | print("保存照片:", str(current_face_dir) + "/" + name +'_' + str(cnt_ss) + ".jpg") 136 | else: 137 | print("请在按 'S' 之前先按 'N' 来建文件夹 / Please press 'N' before 'S'") 138 | # 显示人脸数 / show the numbers of faces detected 139 | cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) 140 | # 添加说明 / add some statements 141 | cv2.putText(img_rd, "Face Register", (20, 40), font, 1, (0, 0, 0), 1, cv2.LINE_AA) 142 | cv2.putText(img_rd, "N: New face folder", (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA) 143 | cv2.putText(img_rd, "S: Save current face", (20, 400), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA) 144 | cv2.putText(img_rd, "Q: Quit", (20, 450), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA) 145 | # 按下 'q' 键退出 / press 'q' to exit 146 | if kk == ord('q'): 147 | break 148 | # 如果需要摄像头窗口大小可调 / uncomment this line if you want the camera window is resizeable 149 | # cv2.namedWindow("camera", 0) 150 | cv2.imshow("camera", img_rd) 151 | # 释放摄像头 / release camera 152 | cap.release() 153 | cv2.destroyAllWindows() 154 | --------------------------------------------------------------------------------