Python+OpenCv+Dlib
#实现人脸检测+表情识别
.xml表情识别库下载
“”"
从USB摄像头中识别人脸,并实时标出面部特征点,简单判断情绪
我在原有程序上增加了串口通讯 因为原来的程序在识别笑脸的时候只是判断上下嘴唇的张合
没有考虑左右嘴角之间的张开变化,这就限制了惊讶表情的识别,在延时上加了多进程函数能
延缓串口的读取速度还不影响表情识别的速度,配合pyserial就可以实现向下位机传输字符串,实现下位机根据上位机的表情检测做出相应的反应。
68个点的识别也是方面我们对表情的判断,可以不显示出来,但是68个点依然可以用来判断表情。
“”"
import threading#多线程
import time
import dlib # 人脸识别的库dlib
import numpy as np # 数据处理的库numpy
import cv2 # 图像处理的库OpenCv
import serial
ser = serial.Serial()
ser.baudrate = 9600
ser.port = 'COM3'
data =''
data1 =''
print(ser)
ser.open()
print(ser)
print(ser.is_open) #检验串口是否打开
class face_emotion():
def __init__(self): # 类在实例化成对象的时候首先调用的方法
# 使用特征提取器get_frontal_face_detector
self.detector = dlib.get_frontal_face_detector()
# dlib的68点模型,使用训练好的特征预测器,加载人脸关键点检测器
self.predictor = dlib.shape_predictor("shape_predictor_68
_face_landmarks.dat")
#用opencv做前脸图像识别
self.face = cv2.CascadeClassifier('haarcascade_frontalface_alt_tree.xml')
# 使用opencv打开usb摄像头。
self.cap = cv2.VideoCapture(0)
def learning_face(self):
# 眉毛直线拟合数据缓冲
line_brow_x = []
line_brow_y = []
# cap.isOpened() 返回true/false 检查初始化是否成功
while (self.cap.isOpened()):
flag, im_rd = self.cap.read()
# 每帧数据延时1ms,延时为0读取的是静态帧
k = cv2.waitKey(1)
# 取灰度
img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
faces = self.face.detectMultiScale(img_gray, 1.1, 5, 0, (100, 100))
# 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects
rects = self.detector(img_gray,0)#将第二个参数增加会改变画质,检测速度会减慢
# 要显示在屏幕上的字体
font = cv2.FONT_HERSHEY_SIMPLEX
i = 0
for (x, y, w, h) in faces:
cv2.rectangle(im_rd, (x, y), (x + w, y + h), (0, 255, 0), 2)
def stop():
print('子进程开始')
data = b'0012'
ser.write(data)
print(data)
time.sleep(1)
data = 0
print('子进程结束')
def stop1():
print('子进程开始')
data = b'0013'
ser.write(data)
print(data)
time.sleep(1)
data = 0
print('子进程结束')
if (len(rects) != 0):
for i in range(len(rects)):
# enumerate方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象
for k, d in enumerate(rects):
# 用红色矩形框出人脸 可显示也可以不显示
# cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255))
# 计算人脸热别框边长
self.face_width = d.right() - d.left()
self.face_higth = d.top() - d.bottom()
# 使用预测器得到68点数据的坐标
shape = self.predictor(im_rd, d)
# 圆圈显示每个特征点
for i in range(0,68):
cv2.circle(im_rd, (shape.part(i).x, shape.part(i).y), 2, (0, 255, 0), -1, 8)
cv2.putText(im_rd, str(i), (shape.part(i).x, shape.part(i).y), cv2.FONT_HERSHEY_SIMPLEX,
0.5,(255, 255, 255))
# 分析点的位置关系来作为表情识别的依据
mouth_width = (shape.part(54).x - shape.part(48).x) / self.face_width # 嘴巴咧开程度 嘴的最右侧减去最左侧
mouth_higth = (shape.part(66).y - shape.part(62).y) / self.face_width # 嘴巴张开程度 上下嘴唇之间的距离
# 通过两个眉毛上的10个特征点,分析挑眉程度和皱眉程度
brow_sum = 0 # 高度之和
frown_sum = 0 # 两边眉毛距离之和
for j in range(17, 21):
brow_sum += (shape.part(j).y - d.top()) + (shape.part(j + 5).y - d.top())
frown_sum += shape.part(j + 5).x - shape.part(j).x
line_brow_x.append(shape.part(j).x)
line_brow_y.append(shape.part(j).y)
tempx = np.array(line_brow_x)
tempy = np.array(line_brow_y)
# np.ployfit(x,a,n)拟合点集a得到n级多项式,其中x为横轴长度
z1 = np.polyfit(tempx, tempy, 1) # 拟合成一次直线
# round(x [,n])返回浮点数x的四舍五入值 round(80.23456, 2)返回80.23
self.brow_k = -round(z1[0], 3) # 拟合出曲线的斜率和实际眉毛的倾斜方向是相反的
brow_hight = (brow_sum / 10) / self.face_width # 眉毛高度占比
brow_width = (frown_sum / 5) / self.face_width # 眉毛距离占比
# print("眉毛高度与识别框高度之比:",round(brow_arv/self.face_width,3))
# print("眉毛间距与识别框高度之比:",round(frown_arv/self.face_width,3))
# 眼睛睁开程度
eye_sum = (shape.part(41).y - shape.part(37).y + shape.part(40).y - shape.part(38).y +shape.part(47).y - shape.part(43).y + shape.part(46).y - shape.part(44).y)
eye_hight = (eye_sum / 4) / self.face_width
eye_center =(shape.part(22).x-shape.part(21).x)/self.face_width
# print("眼睛睁开距离与识别框高度之比:",round(eye_open/self.face_width,3))
# 分情况讨论
# 张嘴,可能是开心或者惊讶
if round(mouth_higth >= 0.03):
if eye_hight >= 0.056:#嘴张开同时眼睛睁大 可以理解为惊讶
cv2.putText(im_rd, "amazing", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX,0.8, (0, 0, 255), 2, 4)
a = threading.Thread(target=stop)
a.start()
if mouth_width >= 0.40:#嘴张开同时嘴角张开 可以理解为开心
print(mouth_width)
cv2.putText(im_rd, "happy", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,(0, 0, 255), 2, 4)
data1 = b'0011'
ser.write(data1)
print(data1)
data1 =0
if eye_center <=0.1:
print(i)
if (len(faces)!=0):#正脸检测 由于侧脸是检测眉心之间的比例是不准确的于是我们判别是在正脸才能进行疲劳监测tired
if round(mouth_higth >= 0.08):#嘴部的张开比例大于0.08时才能判断为打哈欠
print(eye_center)
cv2.putText(im_rd, "tired", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,(0, 0, 255), 2, 4)
a1 = threading.Thread(target=stop1)
a1.start()
else:
cv2.putText(im_rd, "nature", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4)
# 标出人脸数
cv2.putText(im_rd, "Faces: " + str(len(rects)), (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
else:
cv2.putText(im_rd, "No Face", (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
if cv2.waitKey(10) == 27:
break
# 窗口显示
cv2.imshow("camera", im_rd)
self.cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
my_face = face_emotion()
my_face.learning_face()