使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)
在这篇文章中,我将讨论如何创建一个应用程序,在检测到微笑时从网络摄像头捕获自拍照。
流程概述
- 使用Dlib中的面部标志检测器来获取嘴部坐标
- 设置微笑阈值(MAR)
- 访问网络摄像头以设置实时流
- 捕获图像
- 保存图像
- 关闭摄像头
需要的库
- Numpy:用于快速矩阵计算和操作。
- Dlib:包含面部标志的图书馆。
- Cv2:用于图像处理和保存的Open CV库。
- Scipy.spatial :用于计算面部点之间的欧几里德距离。
- Imutils:用于访问视频流的库。
导入库
from scipy.spatial import distance as dist
from imutils.video import VideoStream, FPS
from imutils import face_utils
import imutils
import numpy as np
import time
import dlib
import cv2
面部标志检测器
面部标志检测器是在dlib内部实现的api ,其产生68 (x,y)坐标,其映射到特定的面部结构。
这可以看作如下:
我们将重点放在可以通过点到达的嘴巴上[48,68]
使用dlib可以使用以下代码获得这些特征
shape_predictor =“../shape_predictor_68_face_landmarks.dat”
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(shape_predictor)
(mStart,mEnd)= face_utils.FACIAL_LANDMARKS_IDXS [“mouth”]
mStart,mEnd让我们获得嘴巴的20个坐标中的第一个和最后一个。
您可以在这里下载预训练的坐标文件
http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
微笑特征
嘴是由48到68点给出的
我创建了一个嘴宽高比(MAR)受Soukupova?ech在他们一份2016年的论文,Real-Time Eye Blink Detection using Facial Landmarks定义一个类似眼宽高比(EAR):
EAR的公式为
D = p1和p4之间的距离
L = p2和p6之间和p3和p5之间的距离平均值;
在我们的例子中,MAR被简单地定义为
我们将p1和p7之间的距离计算为D.
我们平均两者之间的距离
- p3和p11
- p4和p10
- p5和p9。
我们称之为L
MAR = L / D.
这是计算MAR的函数:
def smile(mouth):
A = dist.euclidean(mouth[3], mouth[9])
B = dist.euclidean(mouth[2], mouth[10])
C = dist.euclidean(mouth[4], mouth[8])
L = (A+B+C)/3
D = dist.euclidean(mouth[0], mouth[6])
mar=L/D
return mar
在微笑的时候,嘴巴闭上时,p1和p7之间的距离会增加,而顶部和底部的距离会减少,L减小,D增大。而张着嘴笑会导致D增加,L增加。看看当我改变嘴巴形状时MAR是如何变化的。
基于此,我设置了一个微笑<.3或> .38。我可以选择D,因为D会在一个人微笑时总是增加,但D对于所有人来说都不会相同,因为人们有不同的嘴形。
人们应该意识到这些是粗略估计,这可能包括其他情绪,如“ awe”。为了克服这个问题,可以创建一个更高级的模型,该模型考虑更多的面部特征,或者简单地训练基于CV的情绪分类器。
现在我们有一个微笑特征,我们可以实现视频捕获。
视频截取
我们可以使用以下命令使用imutils库访问网络摄像头。cv2.namedWindow创建一个新窗口
vs = VideoStream(src=0).start()
fileStream = False
time.sleep(1.0)
cv2.namedWindow(frame,cv2.WINDOW_NORMAL)
现在我们来到神奇发生的主循环。首先,我们捕获一个单帧,将其转换为灰度(以便于计算),并用于检测面部。cv2.convexHull(嘴)检测嘴部轮廓,cv2.drawContours围绕它绘制绿色轮廓。
while True:
frame = vs.read()
frame = imutils.resize(frame, width=450)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rects = detector(gray, 0)
for rect in rects:
shape = predictor(gray, rect)
shape = face_utils.shape_to_np(shape)
mouth= shape[mStart:mEnd]
mar= smile(mouth)
mouthHull = cv2.convexHull(mouth)
cv2.drawContours(frame, [mouthHull], -1, (0, 255, 0), 1)
提示:此设置也可以在单个帧中检测到多个微笑。
接下来我们设置自动捕获条件
if mar <= .3 or mar > .38 :
COUNTER += 1
else:
if COUNTER >= 15:
TOTAL += 1
frame = vs.read()
time.sleep(0.3)
img_name = “opencv_frame_{}.png”.format(TOTAL)
cv2.imwrite(img_name, frame)
print(“{} written!”.format(img_name))
cv2.destroyWindow(“test”)
COUNTER = 0
在这里,我认为微笑是“selfie worthy”,如果这个人持有它.5秒或30帧。我们检查mar是<.3还是> .38至少15 帧,然后立即保存帧。该文件与名为opencv_frame_.png的代码保存在同一个文件夹中。我添加了一些time.sleep函数来平滑体验。
提示:此部分位于while循环内。
我们还使用cv2.putText函数在框架上打印MAR
cv2.putText(frame, “MAR: {}”.format(mar), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
提示:我有一个30 fps的摄像头,因此我使用的帧数为30.一个可以相应改变。更简单的方法是使用imutils中的FPS函数找到FPS 。
最后,在按下“q”键时放置一个停止视频流的退出命令。
key2 = cv2.waitKey(1) & 0xFF
if key2 == ord(‘q’):
break
最后
cv2.destroyAllWindows()
vs.stop()
效果:
完整的Python代码:
from scipy.spatial import distance as dist
from imutils.video import VideoStream, FPS
from imutils import face_utils
import imutils
import numpy as np
import time
import dlib
import cv2
def smile(mouth):
A = dist.euclidean(mouth[3], mouth[9])
B = dist.euclidean(mouth[2], mouth[10])
C = dist.euclidean(mouth[4], mouth[8])
avg = (A+B+C)/3
D = dist.euclidean(mouth[0], mouth[6])
mar=avg/D
return mar
COUNTER = 0
TOTAL = 0
shape_predictor= "/Users/rra/Downloads/all/shape_predictor_68_face_landmarks.dat"
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(shape_predictor)
(mStart, mEnd) = face_utils.FACIAL_LANDMARKS_IDXS["mouth"]
print("[INFO] starting video stream thread...")
vs = VideoStream(src=0).start()
fileStream = False
time.sleep(1.0)
fps= FPS().start()
cv2.namedWindow("test")
while True:
frame = vs.read()
frame = imutils.resize(frame, width=450)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rects = detector(gray, 0)
for rect in rects:
shape = predictor(gray, rect)
shape = face_utils.shape_to_np(shape)
mouth= shape[mStart:mEnd]
mar= smile(mouth)
mouthHull = cv2.convexHull(mouth)
#print(shape)
cv2.drawContours(frame, [mouthHull], -1, (0, 255, 0), 1)
if mar <= .3 or mar > .38 :
COUNTER += 1
else:
if COUNTER >= 15:
TOTAL += 1
frame = vs.read()
time.sleep(.3)
frame2= frame.copy()
img_name = "opencv_frame_{}.png".format(TOTAL)
cv2.imwrite(img_name, frame)
print("{} written!".format(img_name))
COUNTER = 0
cv2.putText(frame, "MAR: {}".format(mar), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.imshow("Frame", frame)
fps.update()
key2 = cv2.waitKey(1) & 0xFF
if key2 == ord(q):
break
fps.stop()
cv2.destroyAllWindows()
vs.stop()