使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)

Xsens动作捕捉 2023-05-04 3448

在这篇文章中,我将讨论如何创建一个应用程序,在检测到微笑时从网络摄像头捕获自拍照。

流程概述

  1. 使用Dlib中的面部标志检测器来获取嘴部坐标
  2. 设置微笑阈值(MAR)
  3. 访问网络摄像头以设置实时流
  4. 捕获图像
  5. 保存图像
  6. 关闭摄像头

需要的库

  • 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)坐标,其映射到特定的面部结构。

这可以看作如下:

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第1张

我们将重点放在可以通过点到达的嘴巴上[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点给出的

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第2张

我创建了一个嘴宽高比(MAR)受Soukupova?ech在他们一份2016年的论文,Real-Time Eye Blink Detection using Facial Landmarks定义一个类似眼宽高比(EAR):

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第3张

EAR的公式为

D = p1和p4之间的距离

L = p2和p6之间和p3和p5之间的距离平均值;

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第4张

EAR equation

在我们的例子中,MAR被简单地定义为

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第5张

我们将p1和p7之间的距离计算为D.

我们平均两者之间的距离

  • p3和p11
  • p4和p10
  • p5和p9。

我们称之为L

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第6张

MAR方程

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是如何变化的。

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第7张

基于此,我设置了一个微笑<.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()

效果:

使用OpenCV和python自动捕捉微笑的自拍(Python完整实现)  第8张

完整的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()

The End