一、项目内容
1、实验前期工作
收集项目所需资料:
Dlib库:用于人脸检测、特征点检测和特征向量提取
OpenCV库:用于图像处理和视频流处理
Dlib模型文件:shape_predictor_68_face_landmarks.dat 和 dlib_face_recognition_resnet_model_v1.dat
Python安装及配置:确保安装Python 3.10及相关库(Dlib、OpenCV)
配置PyCharm 2024.1的Python解释器和项目环境
安装Python 3.10和相关的库(Dlib, OpenCV, NumPy等)
OpenCV和Python之间的版本对应问题。访问此链接即可找到对应关系Links for opencv-python (tsinghua.edu.cn),如果版本错误,则无法运行
硬件准备:配置好带摄像头的笔记本电脑或者外接摄像头
2、程序编译环境
开发工具:
PyCharm 2024.1 作为开发工具;
使用Python3.10作为解释器环境。
安装库和依赖:
安装Dlib:pip install dlib
安装OpenCV:pip install opencv-python==3.4.16.57
安装其他可能需要的依赖库(如NumPy):pip install numpy
3、算法/框架内容(流程图/框架图)
流程图:
4、实验结果/对比实验
实验结果:
使用笔记本电脑摄像头捕捉的视频流进行人脸检测和特征点检测。
提取128D的特征向量,保存为特征.csv文件以便进行后续的精准的个人识别。
保存和展示检测结果的图像和视频。
如图,检测过程和实验结果展示:
对比实验:
每张人脸都检测,将大大耗费CPU和GPU资源;关键代码如下:
# 对于某张人脸,遍历所有存储的人脸特征 # For every faces detected, compare the faces in the database current_frame_e_distance_list = [] for i in range(len(self.face_feature_known_list)): # 如果 person_X 数据不为空 if str(self.face_feature_known_list[i][0]) != '0.0': e_distance_tmp = self.return_euclidean_distance( self.current_frame_face_feature_list[k], self.face_feature_known_list[i]) logging.debug(" With person %s, the e-distance is %f", str(i + 1), e_distance_tmp) current_frame_e_distance_list.append(e_distance_tmp) else: # 空数据 person_X current_frame_e_distance_list.append(999999999)
若视频中的人脸不出现变化,即不出现“unknown”字样,则不计算和提取特征值进行精准对比。(灵感来自网络)
# 使用中心追踪来识别人脸 def centroid_tracker(self): # current_frame_face_centroid_list当前帧 for i in range(len(self.current_frame_face_centroid_list)): e_distance_current_frame_person_x_list = [] # 对于当前帧中的人脸1, 和上一帧中的 人脸1/2/3/4/.. 进行欧氏距离计算 for j in range(len(self.last_frame_face_centroid_list)): self.last_current_frame_centroid_e_distance = self.return_euclidean_distance( self.current_frame_face_centroid_list[i], self.last_frame_face_centroid_list[j]) e_distance_current_frame_person_x_list.append( self.last_current_frame_centroid_e_distance) last_frame_num = e_distance_current_frame_person_x_list.index( min(e_distance_current_frame_person_x_list)) self.current_frame_face_name_list[i] = self.last_frame_face_name_list[last_frame_num]
5、算法/框架总结
人脸检测:get_frontal_face_detector
框架名称: get_frontal_face_detector
简介: 该方法是Dlib库中实现的人脸检测器,基于方向梯度直方图(Histogram of Oriented Gradients,HOG)特征和支持向量机(Support Vector Machine,SVM)分类器。
功能: 检测图像中的人脸,并返回人脸的矩形边界。
人脸特征定位:shape_predictor
框架名称: shape_predictor
简介: Dlib提供的shape_predictor模型用于人脸特征点检测,通过回归树(Ensemble of Regression Trees)实现。
功能: 在检测到的人脸区域内,预测68个关键特征点的位置,用于进一步的人脸分析和识别。
人脸识别:face_recognition_model_v1
框架名称: face_recognition_model_v1
简介: Dlib的face_recognition_model_v1模型基于深度学习的ResNet(残差网络)结构,用于提取人脸的128D特征向量。
功能: 提取人脸的高维特征向量,供后续的比对和识别使用。
算法总结
方向梯度直方图(HOG)
简介: HOG是一种用于对象检测的特征描述符。通过计算图像局部区域的梯度方向直方图来表征图像的形状和结构。
功能: 在人脸检测过程中,HOG特征用于提取图像中的梯度信息,以便于区分人脸和非人脸区域。
支持向量机(SVM)
简介: SVM是一种监督学习模型,用于分类和回归分析。它通过找到最优超平面来将不同类别的数据分开。
功能: 在人脸检测中,SVM用于根据HOG特征将图像区域分类为人脸和非人脸。
增强回归树(Ensemble of Regression Trees)
简介: 增强回归树是一种集成学习方法,通过组合多个回归树模型来提高预测精度。
功能: 在Dlib的shape_predictor中,增强回归树用于高效准确地定位人脸特征点。
残差网络(ResNet)
简介: ResNet是一种深度卷积神经网络,通过引入残差连接解决深层网络的梯度消失问题。
功能: 在人脸识别模型中,ResNet用于学习并提取人脸的高层次特征。
三元组损失(Triplet Loss)
简介: 三元组损失是一种用于训练深度学习模型的方法,通过最小化相似样本之间的距离并最大化不同样本之间的距离来学习判别特征。
功能: 在人脸识别模型训练过程中,三元组损失用于优化模型,使其能够更好地区分不同个体的特征向量。
二、关键源代码
相机获取训练数据:
# (1)打开摄像头并捕获视频帧: cap = cv2.VideoCapture(0) # 打开笔记本摄像头 self.process(cap) # 运行主方法 # (2) 处理视频帧并显示: def process(self, cap): while True: ret, img_rd = cap.read() # 读取一帧图像 if not ret: break # 按下 'q' 键退出 if cv2.waitKey(1) == ord('q'): break # 控制帧率的刷新,可以关,关闭之后就不能在窗口看见 帧率 self.update_fps() cv2.namedWindow("camera", 1) cv2.imshow("camera", img_rd) # (3) 释放资源并关闭窗口: cap.release() # 释放资源 cv2.destroyAllWindows() # 关闭所有窗口
获取人脸特征:
# (1) 定义 Dlib 的正向人脸检测器、人脸特征点检测器和人脸识别模型: detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor('./model/shape_predictor_68_face_landmarks.dat') face_reco_model = dlib.face_recognition_model_v1("./model/dlib_face_recognition_resnet_model_v1.dat") # (2) 定义函数 return_128d_features,用于从单张图像中提取 128D 特征: def return_128d_features(path_img): img_rd = cv2.imread(path_img) faces = detector(img_rd, 1) # (3) 定义函数 return_features_mean_personX,用于计算一个人脸图像文件夹中所有图像的平均特征向量: def return_features_mean_personX(path_face_person_x): features_list_personX = []
定义主函数 main,用于遍历所有人脸图像文件夹,提取特征并写入 "features_all.csv" 文件:
# 创建并打开features_all.csv文件,将训练的数据写入csv文件 with open("data/features_all.csv", "w", newline="") as csvfile: writer = csv.writer(csvfile) for person in person_list: # 训练并获取在 data里面的person_的图片,将返回的数据写入csv中 logging.info("%sperson_%s", path_images_from_camera, person) features_mean_personX = return_features_mean_personX(path_images_from_camera + person) if len(person.split('_', 2)) == 2: # "person_x" person_name = person else: # "person_x_tom" person_name = person.split('_', 2)[-1] # 大于-1的值 features_mean_personX = np.insert(features_mean_personX, 0, person_name, axis=0) # features_mean_personX.csv文件里将会有129个数据,就是person_name+128位特征数据 writer.writerow(features_mean_personX) logging.info("所有录入人脸数据存入:data/features_all.csv")
测试
从保存的csv文件中读取出数据:
# 从 "features_all.csv" 读取录入人脸特征 def get_face_database(self): if os.path.exists("data/features_all.csv"): path_features_known_csv = "data/features_all.csv" csv_rd = pd.read_csv(path_features_known_csv, header=None) for i in range(csv_rd.shape[0]): features_someone_arr = [] self.face_name_known_list.append(csv_rd.iloc[i][0]) for j in range(1, 129): if csv_rd.iloc[i][j] == '': features_someone_arr.append('0') else: features_someone_arr.append(csv_rd.iloc[i][j]) self.face_feature_known_list.append(features_someone_arr) logging.info("Faces in Database:%d", len(self.face_feature_known_list)) return 1 else: logging.warning("'features_all.csv' not found!") logging.warning("Please run 'get_faces_from_camera.py' " "and 'features_extraction_to_csv.py' before 'face_reco_from_camera.py'") return 0
将检测到的人脸与csv中的数据对比。
# 6.2.2.3 对于某张人脸, 遍历所有存储的人脸特征 for i in range(len(self.face_features_known_list)): # 如果 q 数据不为空 if str(self.face_features_known_list[i][0]) != '0.0': e_distance_tmp = self.return_euclidean_distance( self.current_frame_face_feature_list[k], self.face_features_known_list[i]) logging.info("with person %d, the e-distance: %f", i + 1, e_distance_tmp) self.current_frame_face_X_e_distance_list.append(e_distance_tmp) else: # 空数据 person_X self.current_frame_face_X_e_distance_list.append(7777777)
欢迎讨论或者批评指正
完整代码:
1.本地下载
2.Github