树莓派学习(3)——实现(伪)实时人脸识别

 
 前言
人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术,通常也叫做人像识别、面部识别。

 课设目的

采集摄像头的数据,识别人脸位置,并且和已训练好的数据对比,得到人脸的名字

 课设使用环境

硬件环境:树莓派3B,摄像头模块,5V-2A电源

软件环境:raspbian 4.14.78、python3、face_recognition-1.2.3

dlib-19.16.0、opencv-3.4.3、vncserver

 

 课设内容

主要程序主要分为两部分,第一部分是人脸的编码(train.py),第二部分是人脸的实时识别的实现(rec_face.py)。文件夹dataset是训练数据的文件夹,数据以人名作为文件夹,文件夹里的是那个人的头像图片。文件encodings.pickle是训练后存储的字典数据。

程序主要代码:

1、训练代码(train.py)

import face_recognition
import argparse
import pickle
import cv2
import os
import time
from multiprocessing import Pool

base_path = os.getcwd()
path1 = base_path + "/dataset"
names = os.listdir(path1)
data = {}
known_encodings = []
known_names = []
for name in names:

image_paths = os.listdir(path1 + "/" + name)
for image in image_paths:
image_path = path1 + "/" + name + "/" + image
image_read = cv2.imread(image_path)
rgb = cv2.cvtColor(image_read, cv2.COLOR_BGR2RGB)
print("reading..." + image)
encodings = face_recognition.face_encodings(rgb)
for encoding in encodings:
known_encodings.append(encoding)
known_names.append(name)
data = {"encodings": known_encodings, "names": known_names}
if(len(data)>=1):
print(image)
f = open("encodings.pickle", "wb")
f.write(pickle.dumps(data))
f.close()

2、实时识别的实现(rec_face.py)

from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import cv2
import face_recognition
import pickle
from multiprocessing import Process,Manager,Pool

camera = PiCamera()
camera.resolution = (400,240)
camera.framerate = 2
rawCapture = PiRGBArray(camera, size=(400,240))
i = 0
data = pickle.loads(open("encodings.pickle","rb").read())
names = []
def face_locate(image_1,time_encodings):
ls = face_recognition.face_encodings(image_1)
if(ls!=[]):
time_encodings.append(ls)

def face_rec(image_1,locations):
fa = face_recognition.face_locations(image)
if(fa!=[]):
locations.append(fa)

if __name__ == "__main__":
#获取相机内容
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
time_encodings = Manager().list()
locations = Manager().list()
time_t = time.time()
time_0 = time.time()
image = frame.array
p1 = Process(target=face_locate,args=(image,time_encodings,)) #增加一条子进程
p1.start() #子进程开启
p2 = Process(target=face_rec,args=(image,locations,)) #另一个耗时的方法函数
p2.start()
p1.join() #等待子进程退出
p2.join()
time_1 = time.time()
print("识别时间:" + str(time_1 - time_t))
if(len(locations)!=0):
print(locations)
location = locations[0][0]
print(location)
name = "Unknown"
for time_encoding in time_encodings[0]:
#该函数返回True / False值的列表 ,每个值对应数据集中的每个图像。
# 对于我们数据集中有218个图像,因此返回的列表将具有218个布尔值。
matches = face_recognition.compare_faces(data["encodings"],time_encoding)
name = "Unknown"
if True in matches:
matchedIdxs = [i for (i, b) in enumerate(matches) if b]
counts = {}
for i in matchedIdxs:
name = data["names"][i]
counts[name] = counts.get(name,0)+1
name = max(counts,key=counts.get)
names.append(name)
image = cv2.putText(image,name,(location[3],location[2]),cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 255, 0), 2)
image = cv2.rectangle(image, (location[3], location[0]), (location[1], location[2]), (0, 255, 0), 2)
time_1 = time.time()
print("总时间:"+str(time_1-time_t))
cv2.namedWindow("Frame",0)
cv2.resizeWindow("Frame",600,360)
cv2.imshow("Frame", image)
i+=1
if(i%100==0):
print(i)
key = cv2.waitKey(1) & 0xFF
rawCapture.truncate(0)
if key == ord("q"):
break

 课设结果

 遇到的问题与优化策略
1、Frame窗口的内容在检测到人脸时会卡住,并没有得到实时的更新,
原因应该是整个程序是单线程,调用到系统函数时frame的窗口更新与python使用的是同一个核,并且窗口的优先度低,所以没有得到实时的更新。
2、整个系统的cpu利用率是27%(右上角),使用hop查看后可发现,只有一核工作,三核在围观,原因是现在python使用的是单线程运行,
3、因为树莓派单核性能太低了,而且多核的资源没有被利用上,导致了从识别到人脸,到从辨认出名字的总时间是5.9s左右。对于一个实时的识别来说,这是一个致命的缺陷,接下来的优化是极其重要的。

出现问题的截图:

优化措施:
1、超频提升单核性能由于树莓派被动散热机制,官方采用了比较保守的频率策略:6GHz - 1.2GH。在此基础上,我添加了一个风扇来加强散热。并将最大频率提升到1.4GHz。
改变前:

改变后:

此时运行结果:

可以发现整个流程所用的时间约是4.9s,节省了1s左右,但还是不够快,对比实时来说还有很大差距。

2、多线程编程处理

经过分析,时间使用在定位和编码脸部两个操作中,对应的是face_recognition.face_locations(image)和
face_recognition.encodings(data["encodings"],time_encoding)
这两个操作

经过多线程编程后再次运行的结果如下图,可以发现,时间还是5s,cpu的利用率还是25%,换而言之,程序只用到了一个核。
网上查过资料后,发现python的多线程并不是真正意义上的多线程,解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。换句话说,python的执行机制是:一个程序有多个线程时,多个的线程里,获得GIL锁的线程才有机会执行,对于cpu密集的程序来说,实质上还是单线程。
解决方案:要利用到多核优势,必须用到多进程编程。

经过多线程的调整下:

50%的cpu利用率,平均3s一张图片的处理速率。基本符合本次处理结果。

3、减小图片的质量。原视频流的尺寸是640*480,经测试,调整为400*240后,处理速度有个较大的提升,在普通的时候能有0.8一张图片的速度,识别人脸时也有1.3s一张图片的速度,

七 心得体会
通过本次课设,我跟组员一起完成,我们深刻理解学会如何使用OpenCV、Python和深度学习在图像和视频流中执行人脸识别,在遇到问题时能够对程序进行优化,运行程序达到我们想要的效果。通过在树莓派3B安装摄像头模块,也理解了硬件部分的使用

九 参考资料
1、Adrian Rosebrock  《Face recognition with OpenCV, Python, and deep learning》
2018.06.18 https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/
2、face_recognition项目官网:https://face-recognition.readthedocs.io/en/latest/usage.html
3、 layallan 《树莓派3b基于python3.4 安装opencv3》2017年10月24日
https://blog.csdn.net/layallan/article/details/78328943

githup地址:

发表评论

电子邮件地址不会被公开。 必填项已用*标注