相机实时头部姿态检测(dlib+opencv)

614 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第二十天,点击查看活动详情

----20220625

聊天室更新了八篇,今天我们回到我们之前的头部动作姿态识别。关于姿态识别,我们前面的其实写过一篇关于这个的博客图片头部姿态检测(dlib+opencv) - 掘金 (juejin.cn),也就是我的项目笔记,我们项目主要是关于自闭症检测的,首先对于我们项目进行一个介绍吧

目前,自闭症是一种由多种生物因素引起的大脑广泛性发育障碍疾病,患有自闭症的儿童常常表现出不同程度的言语发育障碍、人际交往障碍、兴趣狭窄和行为方式刻板。这对自闭症儿童与同龄人的交流造成了很大困扰,甚至可能伴随患者终身。目前,针对于自闭症儿童的早期筛查,一直缺少可靠、便捷的测量和评估工具。传统的量表筛查方式,评估周期长,并且很大程度地依赖于家长或治疗师的主观意见,评估结果的准确性缺乏一定的保障。近年来,眼动追踪技术作为一种新兴技术,广泛地被应用在自闭症视觉模式的研究中。大量研究表明,自闭症儿童在面对社交等性质的刺激范式时,常常表现出异于正常儿童的头部动作模式。因此,头部姿态检测这项技术在自闭症的早筛与评估领域有着巨大的潜力。人工智能作为一种新兴技术,正释放出巨大的科技革命和产业变革能量,机器学习是人工智能的一个子集,在医疗保健行业,机器学习已经作为了某些病症的临床决策支持工具,能指导临床医生进行诊断和选择合适的治疗方法,提高护理人员的效率和提升治疗结果。但在自闭症筛查领域中,由于传统筛查方式的限制,整个过程不可避免需要依靠人力来完成,机器学习在该领域的应用显得有些缺乏。所以希望我的项目对于自闭症儿童的筛查有所帮助

在经过研究之后发现,自闭症儿童的yaw,picth,roll较正常儿童有一定的差别,所以重点检测这几个位置的数据值,之后通过监测数据建立模型

image.png

其实之前那篇文章,已经实现了头部姿态检测识别,但是存在一个问题,就是他只能通过照片来进行一个姿态的判定,判定结束之后,将结果输出在输出台,对于正常的自闭症儿童来说,一开始就进行一个拍照,其实不是很合理,于是我们这边现在改为了通过电脑自带摄像头进行识别,相应结果在屏幕上显示

efe605d7-6b26-4399-9399-c2f5a104e78a.png

源码部分

import cv2
import dlib
import numpy as np
from imutils import face_utils

# 脸部特征点模型
face_landmark_path = 'shape_predictor_68_face_landmarks.dat'
# 相机参数,用相机标定程序计算得到
K = [821.4438864, 0.0, 321.59586002,
     0.0, 818.06766317, 222.64326701,
     0.0, 0.0, 1.0]
D = [-1.21551899e-01, 8.80513056e-01, -7.98281873e-03, -3.46685305e-03, -3.83211727e+00]
# 相机矩阵
cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)

# 14个特征点
object_pts = np.float32([[6.825897, 6.760612, 4.402142],
                         [1.330353, 7.122144, 6.903745],
                         [-1.330353, 7.122144, 6.903745],
                         [-6.825897, 6.760612, 4.402142],
                         [5.311432, 5.485328, 3.987654],
                         [1.789930, 5.393625, 4.413414],
                         [-1.789930, 5.393625, 4.413414],
                         [-5.311432, 5.485328, 3.987654],
                         [2.005628, 1.409845, 6.165652],
                         [-2.005628, 1.409845, 6.165652],
                         [2.774015, -2.080775, 5.048531],
                         [-2.774015, -2.080775, 5.048531],
                         [0.000000, -3.116408, 6.097667],
                         [0.000000, -7.415691, 4.070434]])

# 定义显示立方体的顶点
reprojectsrc = np.float32([[10.0, 10.0, 10.0],
                           [10.0, 10.0, -10.0],
                           [10.0, -10.0, -10.0],
                           [10.0, -10.0, 10.0],
                           [-10.0, 10.0, 10.0],
                           [-10.0, 10.0, -10.0],
                           [-10.0, -10.0, -10.0],
                           [-10.0, -10.0, 10.0]])

# 根据立方体顶点定义的边
line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
              [4, 5], [5, 6], [6, 7], [7, 4],
              [0, 4], [1, 5], [2, 6], [3, 7]]


# 获得头部姿态
def get_head_pose(shape):
    # 14个特征点
    image_pts = np.float32([shape[17], shape[21], shape[22], shape[26], shape[36],
                            shape[39], shape[42], shape[45], shape[31], shape[35],
                            shape[48], shape[54], shape[57], shape[8]])
    # 旋转向量与位移向量
    _, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)

    reprojectdst, _ = cv2.projectPoints(reprojectsrc, rotation_vec, translation_vec, cam_matrix,
                                        dist_coeffs)

    reprojectdst = tuple(map(tuple, reprojectdst.reshape(8, 2)))

    # 计算欧拉角Euler
    rotation_mat, _ = cv2.Rodrigues(rotation_vec)
    pose_mat = cv2.hconcat((rotation_mat, translation_vec))
    _, _, _, _, _, _, euler_angle = cv2.decomposeProjectionMatrix(pose_mat)

    return reprojectdst, euler_angle


def main(index):
    # 打开摄像头
    cap = cv2.VideoCapture(index)
    if not cap.isOpened():
        print(f"[IO Error]无法连接至摄像头{index}.")
        return
    # 初始化检测器
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(face_landmark_path)

    while cap.isOpened():
        ret, frame = cap.read()
        if ret:
            face_rects = detector(frame, 0)

            if len(face_rects) > 0:
                shape = predictor(frame, face_rects[0])
                shape = face_utils.shape_to_np(shape)

                reprojectdst, euler_angle = get_head_pose(shape)

                if abs(euler_angle[1, 0]) < 5:
                    color = (0, 255, 0)
                else:
                    color = (0, 0, 255)

                for (x, y) in shape:
                    cv2.circle(frame, (x, y), 1, color, -1)

                for start, end in line_pairs:
                    print(reprojectdst[start])
                    print(reprojectdst[end])
                    cv2.line(frame, (int(reprojectdst[start][0]),int(reprojectdst[start][1])), (int(reprojectdst[end][0]),int(reprojectdst[end][1])), color)

                cv2.putText(frame, "X: " + "{:7.2f}".format(euler_angle[0, 0]), (20, 20), cv2.FONT_HERSHEY_SIMPLEX,
                            0.75, (0, 0, 0), thickness=2)
                cv2.putText(frame, "Y: " + "{:7.2f}".format(euler_angle[1, 0]), (20, 50), cv2.FONT_HERSHEY_SIMPLEX,
                            0.75, (0, 0, 0), thickness=2)
                cv2.putText(frame, "Z: " + "{:7.2f}".format(euler_angle[2, 0]), (20, 80), cv2.FONT_HERSHEY_SIMPLEX,
                            0.75, (0, 0, 0), thickness=2)

            cv2.imshow("Camera Calibration", frame)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                break


if __name__ == '__main__':
    temp = input("请输入待校准的相机编号(数字):")
    temp = int(temp)
    main(temp)