[toc]
快速实现你自己的人脸识别应用
前言
本文代码基于python3+编写,参考
模块github实时识别的实例。face_recognition
github链接在此
是在github上标星几十k的开源项目,能够以优秀的速度,简洁的命令行输入,快速获取你想要获得的人脸信息。face_recongnition
比如:识别图片中人脸的位置,通过对比提供的命令行参数指定的人脸图片信息,来以较高的精度识别出来,博主大三上学期的C#结课大作业就是通过C#做的管理界面来管理提供给Python脚本的图人脸信息,并进行识别标记来达到课堂快速签到的目的。
另外,
还提供了几个函数,能够标记处人脸的关键位置,比如鼻子轮廓,眉毛,嘴唇,人脸轮廓等,(可以通过这个功能来实现实时妆容体验,当然恶搞也是可以的,博主暂时没试过)。face_recongnition
话不多说,先贴代码
安装使用的模块
代码中使用到的库都可以通过
的方式来安装,简单编辑pip install module_name
pip install face_recognition
pip install opencv
pip install numpy
-------命令仅为示例,具体能否运行参考当前各包的安装方法,不过一般都是这个
代码
# -*- coding: UTF-8 -*-
from xml.dom.minidom import parse
import xml.dom.minidom
import face_recognition
import cv2
import numpy as np
import random
from PIL import Image, ImageDraw, ImageFont
# 保存编码数据
known_face_encodings = []
known_face_names = []
# 编码读取到的图片
def FaceEncode(StuInfo, StuInfoSrc):
for src in StuInfoSrc:
my_image = face_recognition.load_image_file("E://pythonXML" + src[1:])
my_face_encoding = face_recognition.face_encodings(my_image)[0]
known_face_encodings.append(my_face_encoding)
for name in StuInfo:
known_face_names.append(name)
if __name__ == '__main__':
# 加载配置文件
DOMTree = parse("E://pythonXML//src//info.xml")
# 获得元素树
collection = DOMTree.documentElement
# 根据person标签获得对应的元素组合
body = collection.getElementsByTagName("person")
StuInfo = []
StuInImgSrc = []
StuDic = []
for stu in body:
stu.setAttribute('here', '0')
name = stu.getElementsByTagName("name")[0]
# print("name: %s" % name.childNodes[0].data)
src = stu.getElementsByTagName("imgsrc")[0]
# print("imgsrc: %s" % src.childNodes[0].data)
StuInfo.append(name.childNodes[0].data)
# 将学生信息添加到一个字典列表中集中管理签到信息
StuDic.append({'name': name.childNodes[0].data, 'IsHere': 0})
StuInImgSrc.append(src.childNodes[0].data)
# 显示读取信息,用于判断是否争取读取文件
# print(StuInfo)
# print(StuInImgSrc)
# print(StuDic)
# 打开摄像头,开始获得数据信息
video_capture = cv2.VideoCapture(0)
# 编码数据信息用于识别
FaceEncode(StuInfo, StuInImgSrc)
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True
cnt = 0
while True:
# 读取一帧
ret, frame = video_capture.read()
# 将原帧信息缩小到四分之一便于处理
small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
# BGR转换成RGB
rgb_small_frame = small_frame[:, :, ::-1]
# 只处理需要处理的帧信息
if process_this_frame:
# 显示出当前帧中所有识别到的人脸
face_locations = face_recognition.face_locations(rgb_small_frame)
face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
face_names = []
for face_encoding in face_encodings:
# 查看是否有数据库中的人脸信息
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
name = "Unknown"
face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
best_match_index = np.argmin(face_distances)
if matches[best_match_index]:
name = known_face_names[best_match_index]
face_names.append(name)
process_this_frame = not process_this_frame
cv2charimg = 0
# 绘制结果
for (top, right, bottom, left), name in zip(face_locations, face_names):
# 探测到人脸的区域是原图像的1/4,所以这里要还原出原大小
top *= 4
right *= 4
bottom *= 4
left *= 4
index = 0
while index < len(StuDic):
stu = StuDic[index]['name']
Ishere = StuDic[index]['IsHere']
if stu == name and Ishere == 0:
print("%s 签到!" % name)
StuDic[index]['IsHere'] = 1
index += 1
# 围绕目标区域画选框
cv2.rectangle(frame, (left, top), (right, bottom), (0, 120, 255), 2)
# 绘制标签 标注名字
cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 130, 255), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
# 显示区域
cv2.imshow("video", frame)
# Q:退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
print(StuDic)
index2 = 0
for stus in body:
print(stus)
while index2 < len(StuDic):
stu = StuDic[index2]['name']
Ishere = StuDic[index2]['IsHere']
print(stu, Ishere)
index2 += 1
if Ishere == 1:
stus.setAttribute('here', '1')
break
try:
with open('E:/pythonXML/src/info.xml', 'w', encoding='UTF-8') as fh:
# 4.writexml()第一个参数是目标文件对象,第二个参数是根节点的缩进格式,第三个参数是其他子节点的缩进格式,
# 第四个参数制定了换行格式,第五个参数制定了xml内容的编码。
DOMTree.writexml(fh, indent='', encoding='UTF-8')
print('OK')
except Exception as err:
print('错误:{err}'.format(err=err))
# 释放摄像头资源
video_capture.release()
cv2.destroyAllWindows()
对比原版代码
原版代码示例在此
import face_recognition
import cv2
import numpy as np
#...省略了原版注释...#
video_capture = cv2.VideoCapture(0)
# Load a sample picture and learn how to recognize it.
obama_image = face_recognition.load_image_file("obama.jpg")
obama_face_encoding = face_recognition.face_encodings(obama_image)[0]
# Load a second sample picture and learn how to recognize it.
biden_image = face_recognition.load_image_file("biden.jpg")
biden_face_encoding = face_recognition.face_encodings(biden_image)[0]
# Create arrays of known face encodings and their names
known_face_encodings = [
obama_face_encoding,
biden_face_encoding
]
known_face_names = [
"Barack Obama",
"Joe Biden"
]
# Initialize some variables
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True
while True:
# Grab a single frame of video
ret, frame = video_capture.read()
# Resize frame of video to 1/4 size for faster face recognition processing
small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
rgb_small_frame = small_frame[:, :, ::-1]
# Only process every other frame of video to save time
if process_this_frame:
# Find all the faces and face encodings in the current frame of video
face_locations = face_recognition.face_locations(rgb_small_frame)
face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
face_names = []
for face_encoding in face_encodings:
# See if the face is a match for the known face(s)
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
name = "Unknown"
# # If a match was found in known_face_encodings, just use the first one.
# if True in matches:
# first_match_index = matches.index(True)
# name = known_face_names[first_match_index]
# Or instead, use the known face with the smallest distance to the new face
face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
best_match_index = np.argmin(face_distances)
if matches[best_match_index]:
name = known_face_names[best_match_index]
face_names.append(name)
process_this_frame = not process_this_frame
# Display the results
for (top, right, bottom, left), name in zip(face_locations, face_names):
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
top *= 4
right *= 4
bottom *= 4
left *= 4
# Draw a box around the face
cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
# Draw a label with a name below the face
cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
# Display the resulting image
cv2.imshow('Video', frame)
# Hit 'q' on the keyboard to quit!
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Release handle to the webcam
video_capture.release()
cv2.destroyAllWindows()
-
通过不修改脚本代码的方式来动态添加删除信息
-
原版示例通过
obama_image = face_recognition.load_image_file("obama.jpg") obama_face_encoding = face_recognition.face_encodings(obama_image)[0] biden_image = face_recognition.load_image_file("biden.jpg") biden_face_encoding = face_recognition.face_encodings(biden_image)[0]
来添加需要识别的人像图片路径,以及人像名字,在需要动态添删信息的时候必然不能直接去修改源代码脚本,所以我们需要将图片信息单独分出来一个文件,因此博主选择了xml,在这
可以了解一下xml,XML 指可扩展标记语言(eXtensible Markup Language), 被设计用来传输和存储数据,重要且简单易学,xml里面除部分保留标签外,用户完全可以自定义自己的标签,让用户更加灵活的使用并传输数据。
-
自定义xml文件
张三 .//img//demo.jpg
.2021
李四 .//img//diao.png2022
王五 .//img//kai.jpg2023
- Xml文件需要一个根标签,如
等等<node></node>
,
<root></root>
- 其余的标签都要写在根标签内,同类标签并列,这很容易理解。
- python可以通过
来解析xml文件,在内存中,python将xml文件读取并解析成一个树状结构,提供了很多方法来读取或者创建写入方法,这可以参考xml.dom官方文档。xml.dom
- Xml文件需要一个根标签,如
-
-
操作并读取xml文件信息,得到图片信息并进行编码
DOMTree = parse("E://pythonXML//src//info.xml") # 获得元素树 collection = DOMTree.documentElement # 根据person标签获得对应的元素组合 body = collection.getElementsByTagName("person")
这里的路径可以参考你自己的文件路径,博主这里使用的绝对路径,也可以使用相对路径。
最后一步返回的是一个可以遍历的集合,这部分python知识读者自行查阅即可,可以参考菜鸟教程
-
对图片进行编码,传入识别函数开始分析并返回人脸位置信息
这一部分参考原版示例代码,可以将编码部分重构成你自己的函数,方便调用
-
好了,我们开始愉快的识别吧
博主这一部分代码参考原版代码并修改了一部分,在控制台实时输出当前识别到的人脸信息并对比xml文件提供的信息,来查看某人是否已经通过识别,读者可以参考注释自行修改。
注意:这里的识别并不需要识别出人脸的运动轨迹,因此并不需要一帧一帧的来识别,只需要处理当前帧处理完瞬间输入的帧信息,以求达到较好的实时效果。
一定要在结束之后释放摄像头资源!!!!
写在最后
博主目前使用的代码启动速度以及结束之后释放资源十分缓慢,不排除是没有使用多线程来加速,也有可能是
模块在内存中加载了大量模型文件以及图片编码信息,这一部分还要多多研究,也欢迎与博主进行交流。face_recognition