[关闭]
@Perfect-Demo 2018-12-17T02:07:38.000000Z 字数 10694 阅读 1647

(14)基于特征点检测的人脸情绪识别

opencv+dlib


这里有两个版本,首先是识别照片中的情绪,然后我调用了自己笔记本上的摄像头来实时对视频的每一帧进行情绪识别

1. 情绪识别类构造

1. 构造函数

首先声明提取人脸的方法,然后用dlib的68个特征点模型进行特征点提取.然后用获取当前文件夹以便于之后对faces文件夹下的照片进行识别.
具体构造函数代码如下:

  1. def __init__(self):
  2. # 使用特征提取器get_frontal_face_detector
  3. self.detector = dlib.get_frontal_face_detector()
  4. # dlib的68点模型,使用作者训练好的特征预测器
  5. self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
  6. current_path = os.getcwd()
  7. self.faces_folder_path = current_path + "/faces/"

2. 人脸识别函数

之后写人脸识别函数.

1. 文件漫步

进行faces文件夹下的文件慢步,对于每一张图片分别进行处理.

2. 检测人脸

用之前声明的人脸检测器进行人脸检测,然后判断是否已经检测到人脸,如果检测到了,就进行下一步,否则,看下一张图.

3. 对人脸进行68特征点检测

进行到这一步说明已经检测到了人脸,然后先用矩形框框出人脸,然后把检测到的68个特征点圈出来.

4. 检测到的特征点计算

这里我们需要定位到我们需要的点
然后来计算一些比例.

下面是代码:

  1. def learning_face(self):
  2. for img_path in glob.glob(os.path.join(self.faces_folder_path, "*.jpg")):
  3. # 眉毛直线拟合数据缓冲
  4. line_brow_x = []
  5. line_brow_y = []
  6. # cap.read()
  7. # 返回两个值:
  8. # 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾
  9. # 图像对象,图像的三维矩阵
  10. im_rd = cv2.imread(img_path, cv2.IMREAD_COLOR)
  11. # 取灰度
  12. img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
  13. # 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects
  14. faces = self.detector(img_gray, 0)
  15. # 待会要显示在屏幕上的字体
  16. font = cv2.FONT_HERSHEY_SIMPLEX
  17. # 如果检测到人脸
  18. if(len(faces)!=0):
  19. # 对每个人脸都标出68个特征点
  20. for i in range(len(faces)):
  21. # enumerate方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象
  22. for k, d in enumerate(faces):
  23. # 用红色矩形框出人脸
  24. cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255))
  25. # 计算人脸热别框边长
  26. self.face_width = d.right() - d.left()
  27. # 使用预测器得到68点数据的坐标
  28. shape = self.predictor(im_rd, d)
  29. # 圆圈显示每个特征点
  30. for i in range(68):
  31. cv2.circle(im_rd, (shape.part(i).x, shape.part(i).y), 2, (0, 255, 0), -1, 8)
  32. #cv2.putText(im_rd, str(i), (shape.part(i).x, shape.part(i).y), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
  33. # (255, 255, 255))
  34. # 分析任意n点的位置关系来作为表情识别的依据
  35. mouth_width = (shape.part(54).x - shape.part(48).x) / self.face_width # 嘴巴咧开程度
  36. mouth_higth = (shape.part(66).y - shape.part(62).y) / self.face_width # 嘴巴张开程度
  37. # print("嘴巴宽度与识别框宽度之比:",mouth_width_arv)
  38. # print("嘴巴高度与识别框高度之比:",mouth_higth_arv)
  39. # 通过两个眉毛上的10个特征点,分析挑眉程度和皱眉程度
  40. brow_sum = 0 # 高度之和
  41. frown_sum = 0 # 两边眉毛距离之和
  42. for j in range(17, 21):
  43. brow_sum += (shape.part(j).y - d.top()) + (shape.part(j + 5).y - d.top())
  44. frown_sum += shape.part(j + 5).x - shape.part(j).x
  45. line_brow_x.append(shape.part(j).x)
  46. line_brow_y.append(shape.part(j).y)
  47. # self.brow_k, self.brow_d = self.fit_slr(line_brow_x, line_brow_y) # 计算眉毛的倾斜程度
  48. tempx = np.array(line_brow_x)
  49. tempy = np.array(line_brow_y)
  50. z1 = np.polyfit(tempx, tempy, 1) # 拟合成一次直线
  51. self.brow_k = -round(z1[0], 3) # 拟合出曲线的斜率和实际眉毛的倾斜方向是相反的
  52. brow_hight = (brow_sum / 10) / self.face_width # 眉毛高度占比
  53. brow_width = (frown_sum / 5) / self.face_width # 眉毛距离占比
  54. # print("眉毛高度与识别框高度之比:",round(brow_arv/self.face_width,3))
  55. # print("眉毛间距与识别框高度之比:",round(frown_arv/self.face_width,3))
  56. # 眼睛睁开程度
  57. eye_sum = (shape.part(41).y - shape.part(37).y + shape.part(40).y - shape.part(38).y +
  58. shape.part(47).y - shape.part(43).y + shape.part(46).y - shape.part(44).y)
  59. eye_hight = (eye_sum / 4) / self.face_width
  60. # print("眼睛睁开距离与识别框高度之比:",round(eye_open/self.face_width,3))
  61. # 分情况讨论
  62. # 张嘴,可能是开心或者惊讶
  63. if round(mouth_higth >= 0.03):
  64. if eye_hight >= 0.056:
  65. cv2.putText(im_rd, "amazing", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  66. (0, 0, 255), 2, 4)
  67. else:
  68. cv2.putText(im_rd, "happy", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  69. (0, 0, 255), 2, 4)
  70. # 没有张嘴,可能是正常和生气
  71. else:
  72. if self.brow_k <= -0.3:
  73. cv2.putText(im_rd, "angry", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  74. (0, 0, 255), 2, 4)
  75. else:
  76. cv2.putText(im_rd, "nature", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  77. (0, 0, 255), 2, 4)
  78. # 标出人脸数
  79. cv2.putText(im_rd, "Faces: "+str(len(faces)), (20,50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
  80. else:
  81. # 没有检测到人脸
  82. cv2.putText(im_rd, "No Face", (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
  83. # 窗口显示
  84. cv2.imshow("camera", im_rd)
  85. # 等待按键,随后退出,销毁窗口
  86. k = cv2.waitKey(0)
  87. cv2.destroyAllWindows()

现在我们已经构建了一个情绪识别的类,我们可以直接创建这个类然后调用这个类里刚写的情绪识别方法来进行照片人脸情绪识别.

下面是两个版本(照片版本和试试视频版本)的完整代码

2. 整合完整代码

1. 照片版本

  1. """
  2. 从照片中识别人脸,并实时标出面部特征点,然后说明检测到的情绪
  3. """
  4. import os
  5. import sys
  6. import dlib
  7. import numpy as np
  8. import cv2
  9. import glob
  10. class face_emotion():
  11. def __init__(self):
  12. # 使用特征提取器get_frontal_face_detector
  13. self.detector = dlib.get_frontal_face_detector()
  14. # dlib的68点模型,使用作者训练好的特征预测器
  15. self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
  16. current_path = os.getcwd()
  17. self.faces_folder_path = current_path + "/faces/"
  18. def learning_face(self):
  19. for img_path in glob.glob(os.path.join(self.faces_folder_path, "*.jpg")):
  20. # 眉毛直线拟合数据缓冲
  21. line_brow_x = []
  22. line_brow_y = []
  23. # cap.read()
  24. # 返回两个值:
  25. # 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾
  26. # 图像对象,图像的三维矩阵
  27. im_rd = cv2.imread(img_path, cv2.IMREAD_COLOR)
  28. # 取灰度
  29. img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
  30. # 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects
  31. faces = self.detector(img_gray, 0)
  32. # 待会要显示在屏幕上的字体
  33. font = cv2.FONT_HERSHEY_SIMPLEX
  34. # 如果检测到人脸
  35. if(len(faces)!=0):
  36. # 对每个人脸都标出68个特征点
  37. for i in range(len(faces)):
  38. # enumerate方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象
  39. for k, d in enumerate(faces):
  40. # 用红色矩形框出人脸
  41. cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255))
  42. # 计算人脸热别框边长
  43. self.face_width = d.right() - d.left()
  44. # 使用预测器得到68点数据的坐标
  45. shape = self.predictor(im_rd, d)
  46. # 圆圈显示每个特征点
  47. for i in range(68):
  48. cv2.circle(im_rd, (shape.part(i).x, shape.part(i).y), 2, (0, 255, 0), -1, 8)
  49. #cv2.putText(im_rd, str(i), (shape.part(i).x, shape.part(i).y), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
  50. # (255, 255, 255))
  51. # 分析任意n点的位置关系来作为表情识别的依据
  52. mouth_width = (shape.part(54).x - shape.part(48).x) / self.face_width # 嘴巴咧开程度
  53. mouth_higth = (shape.part(66).y - shape.part(62).y) / self.face_width # 嘴巴张开程度
  54. # print("嘴巴宽度与识别框宽度之比:",mouth_width_arv)
  55. # print("嘴巴高度与识别框高度之比:",mouth_higth_arv)
  56. # 通过两个眉毛上的10个特征点,分析挑眉程度和皱眉程度
  57. brow_sum = 0 # 高度之和
  58. frown_sum = 0 # 两边眉毛距离之和
  59. for j in range(17, 21):
  60. brow_sum += (shape.part(j).y - d.top()) + (shape.part(j + 5).y - d.top())
  61. frown_sum += shape.part(j + 5).x - shape.part(j).x
  62. line_brow_x.append(shape.part(j).x)
  63. line_brow_y.append(shape.part(j).y)
  64. # self.brow_k, self.brow_d = self.fit_slr(line_brow_x, line_brow_y) # 计算眉毛的倾斜程度
  65. tempx = np.array(line_brow_x)
  66. tempy = np.array(line_brow_y)
  67. z1 = np.polyfit(tempx, tempy, 1) # 拟合成一次直线
  68. self.brow_k = -round(z1[0], 3) # 拟合出曲线的斜率和实际眉毛的倾斜方向是相反的
  69. brow_hight = (brow_sum / 10) / self.face_width # 眉毛高度占比
  70. brow_width = (frown_sum / 5) / self.face_width # 眉毛距离占比
  71. # print("眉毛高度与识别框高度之比:",round(brow_arv/self.face_width,3))
  72. # print("眉毛间距与识别框高度之比:",round(frown_arv/self.face_width,3))
  73. # 眼睛睁开程度
  74. eye_sum = (shape.part(41).y - shape.part(37).y + shape.part(40).y - shape.part(38).y +
  75. shape.part(47).y - shape.part(43).y + shape.part(46).y - shape.part(44).y)
  76. eye_hight = (eye_sum / 4) / self.face_width
  77. # print("眼睛睁开距离与识别框高度之比:",round(eye_open/self.face_width,3))
  78. # 分情况讨论
  79. # 张嘴,可能是开心或者惊讶
  80. if round(mouth_higth >= 0.03):
  81. if eye_hight >= 0.056:
  82. cv2.putText(im_rd, "amazing", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  83. (0, 0, 255), 2, 4)
  84. else:
  85. cv2.putText(im_rd, "happy", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  86. (0, 0, 255), 2, 4)
  87. # 没有张嘴,可能是正常和生气
  88. else:
  89. if self.brow_k <= -0.3:
  90. cv2.putText(im_rd, "angry", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  91. (0, 0, 255), 2, 4)
  92. else:
  93. cv2.putText(im_rd, "nature", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  94. (0, 0, 255), 2, 4)
  95. # 标出人脸数
  96. cv2.putText(im_rd, "Faces: "+str(len(faces)), (20,50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
  97. else:
  98. # 没有检测到人脸
  99. cv2.putText(im_rd, "No Face", (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
  100. # 窗口显示
  101. cv2.imshow("camera", im_rd)
  102. # 等待按键,随后退出,销毁窗口
  103. k = cv2.waitKey(0)
  104. cv2.destroyAllWindows()
  105. if __name__ == "__main__":
  106. my_face = face_emotion()
  107. my_face.learning_face()

2. 视频版本

  1. """
  2. 从视屏中识别人脸,并实时标出面部特征点
  3. """
  4. import dlib
  5. import numpy as np
  6. import cv2
  7. class face_emotion():
  8. def __init__(self):
  9. # 使用特征提取器get_frontal_face_detector
  10. self.detector = dlib.get_frontal_face_detector()
  11. # dlib的68点模型,使用作者训练好的特征预测器
  12. self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
  13. #建cv2摄像头对象,这里使用电脑自带摄像头,如果接了外部摄像头,则自动切换到外部摄像头
  14. self.cap = cv2.VideoCapture(0)
  15. # 设置视频参数,propId设置的视频参数,value设置的参数值
  16. self.cap.set(3, 480)
  17. # 截图screenshoot的计数器
  18. self.cnt = 0
  19. def learning_face(self):
  20. # 眉毛直线拟合数据缓冲
  21. line_brow_x = []
  22. line_brow_y = []
  23. # cap.isOpened() 返回true/false 检查初始化是否成功
  24. while(self.cap.isOpened()):
  25. # cap.read()
  26. # 返回两个值:
  27. # 一个布尔值true/false,用来判断读取视频是否成功/是否到视频末尾
  28. # 图像对象,图像的三维矩阵
  29. flag, im_rd = self.cap.read()
  30. # 每帧数据延时1ms,延时为0读取的是静态帧
  31. k = cv2.waitKey(1)
  32. # 取灰度
  33. img_gray = cv2.cvtColor(im_rd, cv2.COLOR_RGB2GRAY)
  34. # 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects
  35. faces = self.detector(img_gray, 0)
  36. # 待会要显示在屏幕上的字体
  37. font = cv2.FONT_HERSHEY_SIMPLEX
  38. # 如果检测到人脸
  39. if(len(faces)!=0):
  40. # 对每个人脸都标出68个特征点
  41. for i in range(len(faces)):
  42. # enumerate方法同时返回数据对象的索引和数据,k为索引,d为faces中的对象
  43. for k, d in enumerate(faces):
  44. # 用红色矩形框出人脸
  45. cv2.rectangle(im_rd, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255))
  46. # 计算人脸热别框边长
  47. self.face_width = d.right() - d.left()
  48. # 使用预测器得到68点数据的坐标
  49. shape = self.predictor(im_rd, d)
  50. # 圆圈显示每个特征点
  51. for i in range(68):
  52. cv2.circle(im_rd, (shape.part(i).x, shape.part(i).y), 2, (0, 255, 0), -1, 8)
  53. #cv2.putText(im_rd, str(i), (shape.part(i).x, shape.part(i).y), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
  54. # (255, 255, 255))
  55. # 分析任意n点的位置关系来作为表情识别的依据
  56. mouth_width = (shape.part(54).x - shape.part(48).x) / self.face_width # 嘴巴咧开程度
  57. mouth_higth = (shape.part(66).y - shape.part(62).y) / self.face_width # 嘴巴张开程度
  58. # print("嘴巴宽度与识别框宽度之比:",mouth_width_arv)
  59. # print("嘴巴高度与识别框高度之比:",mouth_higth_arv)
  60. # 通过两个眉毛上的10个特征点,分析挑眉程度和皱眉程度
  61. brow_sum = 0 # 高度之和
  62. frown_sum = 0 # 两边眉毛距离之和
  63. for j in range(17, 21):
  64. brow_sum += (shape.part(j).y - d.top()) + (shape.part(j + 5).y - d.top())
  65. frown_sum += shape.part(j + 5).x - shape.part(j).x
  66. line_brow_x.append(shape.part(j).x)
  67. line_brow_y.append(shape.part(j).y)
  68. # self.brow_k, self.brow_d = self.fit_slr(line_brow_x, line_brow_y) # 计算眉毛的倾斜程度
  69. tempx = np.array(line_brow_x)
  70. tempy = np.array(line_brow_y)
  71. z1 = np.polyfit(tempx, tempy, 1) # 拟合成一次直线
  72. self.brow_k = -round(z1[0], 3) # 拟合出曲线的斜率和实际眉毛的倾斜方向是相反的
  73. brow_hight = (brow_sum / 10) / self.face_width # 眉毛高度占比
  74. brow_width = (frown_sum / 5) / self.face_width # 眉毛距离占比
  75. # print("眉毛高度与识别框高度之比:",round(brow_arv/self.face_width,3))
  76. # print("眉毛间距与识别框高度之比:",round(frown_arv/self.face_width,3))
  77. # 眼睛睁开程度
  78. eye_sum = (shape.part(41).y - shape.part(37).y + shape.part(40).y - shape.part(38).y +
  79. shape.part(47).y - shape.part(43).y + shape.part(46).y - shape.part(44).y)
  80. eye_hight = (eye_sum / 4) / self.face_width
  81. # print("眼睛睁开距离与识别框高度之比:",round(eye_open/self.face_width,3))
  82. # 分情况讨论
  83. # 张嘴,可能是开心或者惊讶
  84. if round(mouth_higth >= 0.03):
  85. if eye_hight >= 0.056:
  86. cv2.putText(im_rd, "amazing", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  87. (0, 0, 255), 2, 4)
  88. else:
  89. cv2.putText(im_rd, "happy", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  90. (0, 0, 255), 2, 4)
  91. # 没有张嘴,可能是正常和生气
  92. else:
  93. if self.brow_k <= -0.3:
  94. cv2.putText(im_rd, "angry", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  95. (0, 0, 255), 2, 4)
  96. else:
  97. cv2.putText(im_rd, "nature", (d.left(), d.bottom() + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
  98. (0, 0, 255), 2, 4)
  99. # 标出人脸数
  100. cv2.putText(im_rd, "Faces: "+str(len(faces)), (20,50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
  101. else:
  102. # 没有检测到人脸
  103. cv2.putText(im_rd, "No Face", (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
  104. # 添加说明
  105. im_rd = cv2.putText(im_rd, "S: screenshot", (20, 400), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
  106. im_rd = cv2.putText(im_rd, "Q: quit", (20, 450), font, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
  107. # 按下s键截图保存
  108. if (k == ord('s')):
  109. self.cnt+=1
  110. cv2.imwrite("screenshoot"+str(self.cnt)+".jpg", im_rd)
  111. # 按下q键退出
  112. if(k == ord('q')):
  113. break
  114. # 窗口显示
  115. cv2.imshow("camera", im_rd)
  116. # 释放摄像头
  117. self.cap.release()
  118. # 删除建立的窗口
  119. cv2.destroyAllWindows()
  120. if __name__ == "__main__":
  121. my_face = face_emotion()
  122. my_face.learning_face()
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注