一、主要的流程
二、主要步骤:
1.我们可以通过调用dlib这个库 使用特征提取器get_frontal_face_detector
以及使用训练好的特征预测器 从而得到dlib 的68点模型,为我们的情绪识别提供了很大帮助
# 使用特征提取器get_frontal_face_detector
self.detector = dlib.get_frontal_face_detector()
# dlib 的68点模型,使用训练好的特征预测器
self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
2.
首先 建cv2摄像头对象 之后进行分帧及灰度处理
之所以进行灰度处理是为了减小图像原始数据量,便于后续处理时计算量更少,因为情绪识别的图像处理一般不需要对彩色图像的RGB三个分量都进行处理
之后利用dlib进行检测人脸 并通过预测器得到68点数据的坐标
# 建cv2摄像头对象,这里使用电脑自带摄像头,如果接了外部摄像头,则自动切换到外部摄像头
self.cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
# 设置视频参数,propId设置的视频参数,value设置的参数值
self.cap.set(3, 480)
# cap.isOpened() 返回true/false 检查初始化是否成功
while (True):
# cap.read()
# 返回两个值:
# 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾
# 图像对象,图像的三维矩阵
flag, im_rd = self.cap.read()
# 每帧数据延时1ms,延时为0读取的是静态帧
k = cv2.waitKey(30)
# 取灰度
img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
# 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects
faces = self.detector(img_gray, 0)
# 待会要显示在屏幕上的字体
font = cv2.FONT_HERSHEY_SIMPLEX
# 如果检测到人脸
if (len(faces) != 0):
# 对每个人脸都标出68个特征点
# enumerate方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象
for k, d in enumerate(faces):
# 用红色矩形框出人脸
cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255))
# print(d.top())
# 计算人脸热别框边长
self.face_width = d.right() - d.left()
# 使用预测器得到68点数据的坐标
shape = self.predictor(im_rd, d)
3.到1971年,这两个人研究了 6 种基本表情 (即高兴、悲伤、惊讶、恐惧、愤怒和厌恶 ) ,并系统地建立了上千幅不同的人脸表情图象库。
通过这个表格 我们可以看到 不同的表情对应了脸部不同的运动特征。如惊讶时 一般嘴巴张开 眉毛抬高 眼睛睁大
4.
首先来看一下对于眼睛睁开程度的测定,通过41 37 40 38 等坐标的运算 我们可以得到 眼睛睁开距离与识别框高度之比,我们也可以得到以下参数
mouse_width # 嘴巴宽度与识别框宽度之比
mouse_height # 嘴巴高度与识别框高度之比
k # 眉毛的倾斜方向
eyebrow_height # 眉毛高度与识别框高度之比
eyebrow_spacing # 眉毛间距与识别框高度之比
eye # 眼睛睁开距离与识别框高度之比
eye_long # 眼睛长度
mouth_higth # 嘴巴外圈张开
mouth_xia # 嘴巴下
我们计算了CK+这个人脸表情的数据集里 开心表情的嘴巴张开比例、嘴巴宽度、眉毛倾斜程度等特征,并导入excel表格生成折线图:
通过折线图可以很明显的看出什么参数可以使用,什么参数的可信度不高,什么参数在那个范围内可以作为一个指标。
我们分析了并结合平均值 极值 方差 稳定性等各个方面以及不同表情之间主要突出特征的主成分分析,通过对多个不同表情数据的分析,得出每个指标的参考值,可以写出简单的表情分类标准。
得到了以下代码
if (eye_long <= 0.02 and 0.13 0.21 and mouth_higth< 0.04
and 0 <= mouth_xia_ <= 0.1 and 0.350.43 and
eye_hight <= 0.08):
cv2.putText(im_rd, "angry", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4)
elif 0.03 <= mouth_higth <= 0.3 and (0.3 <= mouth_width <= 0.55)
and (0.08 <= mouth_xia_ <= 0.2 and 0.20.3 and 0.10.22):
cv2.putText(im_rd, "happy", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2, 4)
elif 0.04 0.11 and 0.15 <= mouth_higth_w and 0.20.4
and 00.2 and 0.040.2 and 0.1 # or ((mouth_higth <= 0.1) and (0.12 < eye_hight)):
cv2.putText(im_rd, "surprise", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
(0, 0, 255), 2, 4)
elif ((0.03 <= eye_hight <= 0.08) and 0.27 <= brow_k <= 0.35 and
mouth_xia_ < 0.1 and 0.050.15 and 0.240.41):
# or (0.3 <= mouth_width and eye_hight <= 0.03):
cv2.putText(im_rd, "sadness", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
(0, 0, 255), 2, 4)
elif mouth_xia_<0 and 0.250.4 and 0.040.1 and 0.3
cv2.putText(im_rd, "nature", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
(0, 0, 255), 2, 4)
虽然简单的表情可以识别出来,但是一些复杂的表情识别准确率较低,所以我们决定暂时放弃了对这种方法的探索
于是我们又寻求了另一种分类方式Knn
KNN的全称是K Nearest Neighbors,意思是K个最近的邻居,从这个名字我们就能看出一些KNN算法的蛛丝马迹。K个最近邻居,毫无疑问,K的取值肯定是至关重要的。那么最近的邻居又是怎么回事呢?其实,KNN的原理就是当预测一个新的值x的时候,根据它距离最近的K个点是什么类别来判断x属于哪个类别。听起来有点迷,我们还是来看看图吧。
图中绿色的点就是我们要预测的那个点,假设K=3。那么KNN算法就会找到与它距离最近的三个点(这里用圆圈把它圈起来了),看看哪种类别多一些,比如这个例子中是蓝色三角形多一些,新来的绿色点就归类到蓝三角了。
但是,当K=5的时候,判定就变成不一样了。这次变成红圆多一些,所以新来的绿点被归类成红圆。从这个例子中,我们就能看得出K的取值是很重要的。
就先介绍到这了
这是我们的一个课程设计
我们一步一步探索 表情识别部分由sklearn knn模型识别
我们也制作了交互页面gui 包括 摄像头实时识别及图片表情识别
完整代码 可私信我