Python 3 利用 Dlib 实现摄像头实时人脸检测和平铺显示
1. 引言
在某些场景下,我们不仅需要进行实时人脸检测追踪,还要进行再加工;这里进行摄像头实时人脸检测,并对于实时检测的人脸进行初步提取;
学习Python中有不明白推荐加入交流群
号:864573496群里有志同道合的小伙伴,互帮互助,群里有不错的视频学习教程和PDF!每晚8:00群里直播
单个/多个人脸检测,并依次在摄像头窗口,实时平铺显示检测到的人脸;
图 1 动态实时检测效果图
检测到的人脸矩形图像,会依次平铺显示 在摄像头的左上方;
当多个人脸时候,也能够依次铺开显示;
左上角窗口的大小会根据捕获到的人脸大小实时变化;
图 2 单个/多个人脸情况下摄像头识别显示结果
2. 代码实现
主要分为三个部分:
摄像头调用,利用 OpenCv 里面的cv2.VideoCapture() ;
人脸检测, 这里利用开源的 Dlib 框架,Dlib 中人脸检测具体可以参考Python 3 利用 Dlib 19.7 进行人脸检测;
图像填充,剪切部分可以参考Python 3 利用 Dlib 实现人脸检测和剪切;
2.1 摄像头调用
Python 中利用 OpenCv 调用摄像头的一个例子how_to_use_camera.py:
1# OpenCv 调用摄像头 2# 默认调用笔记本摄像头 3 4# Author: coneypo 5# Blog: http://www.cnblogs.com/AdaminXie 6# GitHub: https://github.com/coneypo/Dlib_face_cut 7# Mail: coneypo@foxmail.com 8 9import cv21011cap = cv2.VideoCapture(0)1213# cap.set(propId, value)14# 设置视频参数: propId - 设置的视频参数, value - 设置的参数值15cap.set(3, 480)1617# cap.isOpened() 返回 true/false, 检查摄像头初始化是否成功18print(cap.isOpened())1920# cap.read()21"""22返回两个值23 先返回一个布尔值, 如果视频读取正确, 则为 True, 如果错误, 则为 False; 24 也可用来判断是否到视频末尾;2526 再返回一个值, 为每一帧的图像, 该值是一个三维矩阵;2728 通用接收方法为: 29 ret,frame = cap.read();30 ret: 布尔值;31 frame: 图像的三维矩阵;32 这样 ret 存储布尔值, frame 存储图像;3334 若使用一个变量来接收两个值, 如:35 frame = cap.read()36 则 frame 为一个元组, 原来使用 frame 处需更改为 frame[1]37"""3839while cap.isOpened():40ret_flag, img_camera = cap.read()41cv2.imshow("camera", img_camera)4243# 每帧数据延时 1ms, 延时为0, 读取的是静态帧44k = cv2.waitKey(1)4546# 按下 's' 保存截图47ifk == ord('s'):48cv2.imwrite("test.jpg", img_camera)4950# 按下 'q' 退出51ifk == ord('q'):52break5354# 释放所有摄像头55cap.release()5657# 删除建立的所有窗口58cv2.destroyAllWindows()
2.2 人脸检测
利用 Dlib 正向人脸检测器,dlib.get_frontal_face_detector();
对于本地人脸图像文件,一个利用 Dlib 进行人脸检测的例子:
face_detector_v2_use_opencv.py:
1# created at 2017-11-27 2# updated at 2018-09-06 3 4# Author: coneypo 5# Dlib: http://dlib.net/ 6# Blog: http://www.cnblogs.com/AdaminXie/ 7# Github: https://github.com/coneypo/Dlib_examples 8 9# create object of OpenCv10# use OpenCv to read and show images1112import dlib13import cv21415# 使用 Dlib 的正面人脸检测器 frontal_face_detector16detector = dlib.get_frontal_face_detector()1718# 图片所在路径19# read image20img = cv2.imread("imgs/faces_2.jpeg")2122# 使用 detector 检测器来检测图像中的人脸23# use detector of Dlib to detector faces24faces = detector(img, 1)25print("人脸数 / Faces in all: ", len(faces))2627# Traversal every face28fori, din enumerate(faces):29print("第", i+1,"个人脸的矩形框坐标:",30"left:", d.left(),"right:", d.right(),"top:", d.top(),"bottom:", d.bottom())31cv2.rectangle(img, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 255), 2)3233cv2.namedWindow("img", 2)34cv2.imshow("img", img)35cv2.waitKey(0)
图 3 参数 d.top(), d.right(), d.left(), d.bottom() 位置坐标说明
2.3 图像裁剪
如果想访问图像的某点像素,对于 opencv 对象可以利用索引 img [height] [width]:
存储像素其实是一个三维数组,先高度 height,然后宽度 width;
返回的是一个颜色数组( 0-255,0-255,0-255 ),按照( B, G, R )的顺序;
比如蓝色就是(255,0,0),红色是(0,0,255);
所以要做的就是对于检测到的人脸,要依次平铺填充到摄像头显示的实时帧 img_rd 中;
所以进行图像裁剪填充这块的代码如下(注意要防止截切平铺的图像不能超出 640x480 ):
# 检测到人脸iflen(faces) != 0:
# 记录每次开始写入人脸像素的宽度位置faces_start_width = 0
forfacein faces:
# 绘制矩形框 cv2.rectangle(img_rd, tuple([face.left(), face.top()]), tuple([face.right(), face.bottom()]),
(0, 255, 255), 2)
height = face.bottom() - face.top()
width = face.right() - face.left()
### 进行人脸裁减 #### 如果没有超出摄像头边界if(face.bottom() < 480)and(face.right() < 640)and \
((face.top() + height) < 480)and((face.left() + width) < 640):
# 填充foriin range(height):
forjin range(width):
img_rd[i][faces_start_width + j] = \
img_rd[face.top() + i][face.left() + j]
# 更新 faces_start_width 的坐标faces_start_width += width
记得要更新 faces_start_width 的坐标,达到依次平铺的效果:
图 4 平铺显示的人脸
2.4. 完整源码
1# 调用摄像头实时单个/多个人脸检测,并依次在摄像头窗口,实时平铺显示检测到的人脸; 2 3# Author: coneypo 4# Blog: http://www.cnblogs.com/AdaminXie 5# GitHub: https://github.com/coneypo/Dlib_face_cut 6 7import dlib 8import cv2 9import time1011# 储存截图的目录12path_screenshots ="data/images/screenshots/"1314detector = dlib.get_frontal_face_detector()15predictor = dlib.shape_predictor('data/dlib/shape_predictor_68_face_landmarks.dat')1617# 创建 cv2 摄像头对象18cap = cv2.VideoCapture(0)1920# 设置视频参数,propId 设置的视频参数,value 设置的参数值21cap.set(3, 960)2223# 截图 screenshots 的计数器24ss_cnt = 02526while cap.isOpened():27flag, img_rd = cap.read()2829# 每帧数据延时 1ms,延时为 0 读取的是静态帧30k = cv2.waitKey(1)3132# 取灰度33img_gray = cv2.cvtColor(img_rd, cv2.COLOR_RGB2GRAY)3435# 人脸数36faces = detector(img_gray, 0)3738# 待会要写的字体39font = cv2.FONT_HERSHEY_SIMPLEX4041# 按下 'q' 键退出42ifk == ord('q'):43break44else:45# 检测到人脸46iflen(faces) != 0:47# 记录每次开始写入人脸像素的宽度位置48faces_start_width = 04950forfacein faces:51# 绘制矩形框52 cv2.rectangle(img_rd, tuple([face.left(), face.top()]), tuple([face.right(), face.bottom()]),53(0, 255, 255), 2)5455height = face.bottom() - face.top()56width = face.right() - face.left()5758### 进行人脸裁减 ###59# 如果没有超出摄像头边界60if(face.bottom() < 480)and(face.right() < 640)and \61((face.top() + height) < 480)and((face.left() + width) < 640):62# 填充63foriin range(height):64forjin range(width):65img_rd[i][faces_start_width + j] = \66img_rd[face.top() + i][face.left() + j]6768# 更新 faces_start_width 的坐标69faces_start_width += width7071cv2.putText(img_rd,"Faces in all: "+ str(len(faces)), (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)7273else:74# 没有检测到人脸75cv2.putText(img_rd,"no face", (20, 350), font, 0.8, (0, 0, 0), 1, cv2.LINE_AA)7677# 添加说明78img_rd = cv2.putText(img_rd,"Press 'S': Screen shot", (20, 400), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)79img_rd = cv2.putText(img_rd,"Press 'Q': Quit", (20, 450), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA)8081# 按下 's' 键保存82ifk == ord('s'):83ss_cnt += 184print(path_screenshots +"screenshot"+"_"+ str(ss_cnt) +"_"+ time.strftime("%Y-%m-%d-%H-%M-%S",85time.localtime()) +".jpg")86cv2.imwrite(path_screenshots +"screenshot"+"_"+ str(ss_cnt) +"_"+ time.strftime("%Y-%m-%d-%H-%M-%S",87time.localtime()) +".jpg",88 img_rd)8990cv2.namedWindow("camera", 1)91cv2.imshow("camera", img_rd)9293# 释放摄像头94cap.release()9596# 删除建立的窗口97cv2.destroyAllWindows()
网友评论