前言
1.编写目的
一方面是分享思路(啥都搜不到真的好痛苦)
另一方面是记录一下,方便自己以后查(感谢提供资料的师哥师姐们和老师)
2.实验环境
操作系统:Win10
IDE:PyCharm 2021.3 (Community Edition)
Python:3.8
3.实现内容
捕捉面部关键点并实时视频展示,同时计算头部旋转角度并将数据存入mysql数据库;
通过flask驱动上述python程序运行,并读取其结果的mysql数据展示到前端页面,并添加echarts作图可视化。
4.实现过程
- 理解vtuber.py程序(捕捉面部关键点并视频绘制展示),为此给vtuber.py程序加了详细注释(数学计算方面的代码大脑自动略过了)
import cv2
import dlib
import numpy as np
DETECTOR = dlib.get_frontal_face_detector() # 获得脸部位置检测器
PREDICTOR = dlib.shape_predictor('./data/shape_predictor_68_face_landmarks.dat') # 获取人脸64关键点检测器
# 使⽤Dlib提取⾯部关键点
## 在img中寻找人脸,获取最大的人脸框的位置信息
def face_positioning(img):
dets = DETECTOR(img, 0) # 0表示采样(upsample)次数:0识别的人脸少点,1识别的多点,2识别的更多,小脸也可以识别
if not dets:
return None
return max(dets, key=lambda det: (det.right() - det.left()) * (det.bottom() - det.top())) # 返回面积最大的那张脸
## 提取面部关键点位置信息
def extract_key_points(img, position):
landmark_shape = PREDICTOR(img, position) # 寻找人脸的68个标定点
key_points = []
# 将68个关键点,每个点的x、y都以数组形式[x y]存入key_points中,并返回
for i in range(68):
pos = landmark_shape.part(i)
key_points.append(np.array([pos.x, pos.y], dtype=np.float32))
return key_points
# 计算面部特征
## 计算头部旋转三角形三个顶点(眉心、下巴、鼻子),每个点的中心位置
def generate_points(key_points): # 生成构造点
def center(array):
return sum([key_points[i] for i in array]) / len(array) # 得到相应部位的中点(应该是数组的计算吧,所以能够同时算出x和y的平均值,得到中点的数组[x y])
left_brow = [18, 19, 20, 21] # 左眉包含第18、19、20、21个特征点,下面同理
right_brow = [22, 23, 24, 25] # 右眉
chin = [6, 7, 8, 9, 10] # 下巴
nose = [29, 30] # 鼻子
# 返回眉心、下巴、鼻子三个部位,每个部位的中点的位置数组
return center(left_brow + right_brow), center(chin), center(nose)
## 计算头部旋转角度
def generate_features(contruction_points): # 生成特征
brow_center, chin_center, nose_center = contruction_points
# 边向量
mid_edge = brow_center - chin_center # 眉心和下巴连线的连线
bevel_edge = brow_center - nose_center # 眉心和鼻子的连线
# 计算长度
mid_edge_length = np.linalg.norm(mid_edge) # np.linalg.norm求范数
# 计算头部水平旋转角度(计算 mid_edge 与 bevel_edge 向量积,可得到以这两个向量为边的平⾏四边形⾯积;⿐⼦转过的⾼度 = 平⾏四边形⾯积 / 中线⻓度)
horizontal_rotation = np.cross(mid_edge, bevel_edge) / mid_edge_length ** 2 # 高与底的比值
# 计算头部垂直旋转角度(计算 mid_edge 与 bevel_edge 数量积,再除以两次中线⻓度可以得到⽐值)
vertical_rotation = mid_edge @ bevel_edge / mid_edge_length ** 2 # @ 点乘
return np.array([horizontal_rotation, vertical_rotation])
# 绘图
## 手绘窗口
def draw_image(h_rotation, v_rotation): # 画脸
img = np.ones([512, 512], dtype=np.float32) # [512,512]大小的画面
face_length = 200
center = 256, 256 # 画面中心
left_eye = int(220 - h_rotation * face_length), int(
249 + v_rotation * face_length) # h_rotation: 水平旋转量,v_rotation: 垂直旋转量
right_eye = int(292 - h_rotation * face_length), int(249 + v_rotation * face_length)
month = int(256 - h_rotation * face_length / 2), int(310 + v_rotation * face_length / 2)
# 画出脸和三个部位
cv2.circle(img, center, 100, 0, 1) # 参数分别为图片、圆心、半径、颜色、圆形轮廓的粗细(如果为正,负厚度表示要绘制实心圆)
cv2.circle(img, left_eye, 15, 0, 1)
cv2.circle(img, right_eye, 15, 0, 1)
cv2.circle(img, month, 5, 0, 1)
return img
## 摄像头窗口图像处理
def extract_img_features(img): # 提取图片特征
# 获取人脸位置信息
face_position = face_positioning(img)
# 如果没有获取到面部关键点(没有检测到脸)的时候,输出原图像(摄像头原画面)
if not face_position:
cv2.imshow('self', img)
cv2.waitKey(1)
return None
# 如果获取了面部关键点(检测到脸)了的时候:
key_points = extract_key_points(img, face_position) # 根据人脸位置,获取面部关键点位置信息[x y]
# 在图像上,每个关键点的位置上,绘制信息(第i个)
for i, (p_x, p_y) in enumerate(key_points): # enumerate使得key_points中每个元素具有索引i
cv2.putText(img, str(i), (int(p_x), int(p_y)),
cv2.FONT_HERSHEY_COMPLEX, 0.25,
(255, 255, 255)) # cv2.FONT_HERSHEY_COMPLEX及其往后分别是字体样式、字体大小、字体颜色、(字体粗细)
# 在图像上,绘制出头部旋转三角形三个点的位置(标上0,1,2)
construction_points = generate_points(key_points) # 获取头部旋转三角形三个部位的中点位置数组
for i, (p_x, p_y) in enumerate(construction_points):
cv2.putText(img, str(i), (int(p_x), int(p_y)),
cv2.FONT_HERSHEY_COMPLEX, 0.25, (255, 0, 0))
# 计算头部水平、垂直旋转角度,并绘制在图上的鼻子位置
rotation = generate_features(construction_points)
cv2.putText(img, str(rotation),
(int(construction_points[-