头部姿态估计算法原理

news2024/11/28 10:47:36

人脸检测

文章目录

  • 人脸检测
  • 一、前言
  • 二、原理
  • 三、代码实现


一、前言

头部姿态估计是通过一幅面部图像来获得头部的姿态角.
在3D空间中,表示物体的旋转可以由三个欧拉角(Euler Angle)来表示:分别计算 pitch(围绕X轴旋转),yaw(围绕Y轴旋转) 和 roll(围绕Z轴旋转) ,分别学名俯仰角、偏航角和滚转角,通俗讲就是抬头、摇头和转头,示意图如下:
在这里插入图片描述

二、原理

若对相机标定熟悉的话,就比较好理解,因为 Head Pose Estimation 比较有难度的部分已经被大牛们搞定了,一种比较经典的 Head Pose Estimation 算法的步骤一般为:
(1)2D人脸关键点检测;
(2)3D人脸模型匹配;
(3)求解3D点和对应2D点的转换关系;
(4)根据旋转矩阵求解欧拉角。
众所周知一个物体相对于相机的姿态可以使用旋转矩阵和平移矩阵来表示:
(1)平移矩阵:物体相对于相机的空间位置关系矩阵,用T表示;
(2)旋转矩阵:物体相对于相机的空间姿态关系矩阵,用R表示.
看来必然少不了坐标系转换,分别是:世界坐标系(UVW)、相机坐标系(XYZ)、图像中心坐标系(uv)和像素坐标系(xy),如下图:
在这里插入图片描述
世界坐标系到相机坐标系:
在这里插入图片描述
相机坐标系到像素坐标系:
在这里插入图片描述
像素坐标系和世界坐标系的关系如下:
在这里插入图片描述
上式的求解可用DLT(Direct Linear Transform)算法结合最小二乘进行迭代求解, 相机总有点瑕疵,比如径向和切向畸变,那关系就要稍微复杂一些,相机坐标系要先转换到图像中心坐标系, 图像中心坐标系到像素坐标系:
在这里插入图片描述
确定pose就是:确定从3D model到图片中人脸的仿射变换矩阵,它包含旋转和平移的信息;看来只要知道世界坐标系内点的位置、像素坐标位置和相机参数就可以搞定旋转和平移矩阵,关系分明是非线性的,其实OpenCV已经给我们提供了求解PnP问题的函数solvePnp(),它的输出结果包括旋转向量(roatation vector)和平移向量(translation vector),只关心旋转信息,所以主要将对 roatation vector进行操作。

得到旋转矩阵后,就可以得到欧拉角了。rotation vector 是物体旋转信息的表示方式之一,是OpenCV常用的表示方式。除了rotation vector还有欧拉角(Euler angle)、旋转矩阵(Rotation Matrix)、方向余弦矩阵(Direction Cosine Matrix)、四元数(Quaternion) 和 轴-角表示(Axis-Angle)。 因为我需要的是欧拉角,所以这里只介绍将rotation vector 转换为欧拉角的方法。

三维空间的任意旋转,都可以用绕三维空间的某个轴旋转过某个角度来表示,即Axis-Angle表示方法。Axis可用一个三维向量(x,y,z)来表示,theta可以用一个角度值来表示,直观来讲,一个四维向量(theta,x,y,z)就可以表示出三维空间任意的旋转。
注意,这里的三维向量(x,y,z)只是用来表示axis的方向朝向,因此更紧凑的表示方式是用一个单位向量来表示方向axis,而用该三维向量的长度来表示角度值theta。这样以来,可以用一个三维向量(thetax,thetay, theta*z)就可以表示出三维空间任意的旋转,前提是其中(x,y,z)是单位向量,这就是旋转向量(Rotation Vector)的表示方式。

四元数(Quaternion)也是一种常用的旋转表示方式。从四元数转换到欧拉角公式较简单,所以我先将rotation vector转换为四元数。假设(x,y,z)是axis方向的单位向量,theta是绕axis转过的角度,那么四元数可以表示为:
在这里插入图片描述
四元数到欧拉角的转换公式如下:
在这里插入图片描述
arctan和arcsin的结果为[-pi/2,pi/2],不能覆盖所有的欧拉角,因此采用atan2代替arctan:
在这里插入图片描述

三、代码实现

import cv2
import math
import numpy as np
 
 
def face_orientation(frame, landmarks):
    size = frame.shape  # (height, width, color_channel)
 
    image_points = np.array([
                            (landmarks[4], landmarks[5]),     # Nose tip
                            (landmarks[10], landmarks[11]),   # Chin
                            (landmarks[0], landmarks[1]),     # Left eye left corner
                            (landmarks[2], landmarks[3]),     # Right eye right corne
                            (landmarks[6], landmarks[7]),     # Left Mouth corner
                            (landmarks[8], landmarks[9])      # Right mouth corner
                        ], dtype="double")
 
 
 
 
    model_points = np.array([
                            (0.0, 0.0, 0.0),             # Nose tip
                            (0.0, -330.0, -65.0),        # Chin
                            (-165.0, 170.0, -135.0),     # Left eye left corner
                            (165.0, 170.0, -135.0),      # Right eye right corne
                            (-150.0, -150.0, -125.0),    # Left Mouth corner
                            (150.0, -150.0, -125.0)      # Right mouth corner
                        ])
 
    # Camera internals
 
    center = (size[1]/2, size[0]/2)
    focal_length = center[0] / np.tan(60/2 * np.pi / 180)   # 焦距
    camera_matrix = np.array(
                         [[focal_length, 0, center[0]],
                         [0, focal_length, center[1]],
                         [0, 0, 1]], dtype="double"
                         )
 
    dist_coeffs = np.zeros((4, 1))  # Assuming no lens distortion   (距离系数/假设没有镜头失真)
    # 计算旋转矩阵rotation_vector和平移矩阵translation_vector
    (success, rotation_vector, translation_vector) = cv2.solvePnP(model_points, image_points, camera_matrix, dist_coeffs, flags=cv2.CV_ITERATIVE)
 
 
    axis = np.float32([[500, 0, 0],
                          [0, 500, 0],
                          [0, 0, 500]])
 
    imgpts, jac = cv2.projectPoints(axis, rotation_vector, translation_vector, camera_matrix, dist_coeffs)
    modelpts, jac2 = cv2.projectPoints(model_points, rotation_vector, translation_vector, camera_matrix, dist_coeffs)
    rvec_matrix = cv2.Rodrigues(rotation_vector)[0]
 
    proj_matrix = np.hstack((rvec_matrix, translation_vector))
    eulerAngles = cv2.decomposeProjectionMatrix(proj_matrix)[6]
 
 
    pitch, yaw, roll = [math.radians(_) for _ in eulerAngles]
 
 
    pitch = math.degrees(math.asin(math.sin(pitch)))
    roll = -math.degrees(math.asin(math.sin(roll)))
    yaw = math.degrees(math.asin(math.sin(yaw)))
 
    return imgpts, modelpts, (str(int(roll)), str(int(pitch)), str(int(yaw))), (landmarks[4], landmarks[5])
 
f = open('/home/lwl/test/landmark.txt','r')
for line in iter(f):
    img_info = line.split(' ')
    img_path = img_info[0]
    frame = cv2.imread(img_path)
    landmarks = map(int, img_info[1:])
 
    print(img_path)
    imgpts, modelpts, rotate_degree, nose = face_orientation(frame, landmarks)
 
    cv2.line(frame, nose, tuple(imgpts[1].ravel()), (0, 255, 0), 3)  # GREEN
    cv2.line(frame, nose, tuple(imgpts[0].ravel()), (255, 0, 0), 3)  # BLUE
    cv2.line(frame, nose, tuple(imgpts[2].ravel()), (0, 0, 255), 3)  # RED
 
    remapping = [2,3,0,4,5,1]
    for index in range(len(landmarks)/2):
        random_color = tuple(np.random.random_integers(0,255,size=3))
 
        cv2.circle(frame, (landmarks[index*2], landmarks[index*2+1]), 5, random_color, -1)
        cv2.circle(frame,  tuple(modelpts[remapping[index]].ravel().astype(int)), 2, random_color, -1)
 
 
#    cv2.putText(frame, rotate_degree[0]+' '+rotate_degree[1]+' '+rotate_degree[2], (10, 30),
#                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0),
#                thickness=2, lineType=2)
 
    for j in xrange(len(rotate_degree)):
                cv2.putText(frame, ('{:05.2f}').format(float(rotate_degree[j])), (10, 30 + (50 * j)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), thickness=2, lineType=2)
 
    cv2.imwrite('test/'+img_path.split('/')[1], frame)
 
f.close()

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1392706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络新手必读!CentOS 7系统IP配置与防火墙管理攻略

前言 掌握CentOS 7系统的网络配置技能,从IP地址、子网掩码、DNS到防火墙,一篇文章全搞定!无论你是网络新手还是寻求深入了解的高手,这篇文章都为你提供了简单易懂的配置指南,助你轻松驾驭系统网络。摆脱繁琐的配置困扰…

C# 图解教程 第5版 —— 第23章 异常

文章目录 23.1 什么是异常23.2 try 语句23.3 异常类23.4 catch 子句23.5 异常过滤器23.6 catch 子句段23.7 finally 块23.8 为异常寻找处理程序23.9 进一步搜索23.9.1 一般法则23.9.2 搜索调用栈的示例(*) 23.10 抛出异常23.11 不带异常对象的抛出23.12 …

基于变换域的模版匹配

模板匹配原理 图像的空间域与其他域之间的变换,如傅里叶变换,小波变换,轮廓波变换,剪切波变换等,实际上是图像在其他坐标领域中的表现。在空间域中,图像的信息是像素值和坐标位置;在其他域中&a…

快速上手!LLaMa-Factory最新微调实践,轻松实现专属大模型

1.为什么要对Yuan2.0做微调? Yuan2.0(https://huggingface.co/IEITYuan)是浪潮信息发布的新一代基础语言大模型,该模型拥有优异的数学、代码能力。自发布以来,Yuan2.0已经受到了业界广泛的关注。当前Yuan2.0已经开源…

day19【LeetCode力扣】160.相交链表

day19【LeetCode力扣】160.相交链表 1.题目描述 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始相交**:** 题目数据 保证 整个链…

基于MATLAB计算无线通信覆盖(一)环境准备

一、环境 MATLAB 2022b 注:开始仿真前需部署地理坐标区和地理图,最好采用第三种,直接把底图数据下载到本地,防止连接不上网络时只能显示darkwater的底图。 可用于地理坐标区和地理图的底图如下表所示 二、下载底图并安装 工具&…

Intel开发环境Quartus、Eclipse与WSL的安装

PC :win10 64bit 安装顺序:先安装Quartus 21.4,接着Eclipse或者WSL(Windows Subsystem for Linux),Eclipse与WSL的安装不分先后。 为什么要安装Eclipse? 因为Eclipse可以开发基于Nios II的C/…

RabbitMQ使用篇

☆* o(≧▽≦)o *☆嗨~我是小奥🍹 📄📄📄个人博客:小奥的博客 📄📄📄CSDN:个人CSDN 📙📙📙Github:传送门 📅&a…

CTF CRYPTO 密码学-1

题目名称:enc 题目描述: 压缩包中含两个文件:一个秘钥d.dec,一个密文flag.enc 解题过程: Step1:这题是一个解密他题目,尝试openssl去ras解密 工具简介 在Kali Linux系统中,openss…

交友系统程序开发,前后端源码交付,允许二开,UI配色新颖,APP小程序H5随心搭配!

在开发设计与测试阶段,主要包括了程序开发、测试和上线运营等环节。根据产品经理确定的功能需求,开发团队开始进行具体的编程和开发工作。这个过程中需要考虑到不同设备、不同系统和不同版本的需求,确保软件可以在不同平台上稳定运行。 测试是…

通过指令反向翻译进行自我对齐

1、写作动机: 对齐大型语言模型以执行指导性任务通常需要在大量人工标注的指令或偏好信息上进行微调,然而,使用此类高质量数据对指令遵循任务进行标注是难以扩展的。 2、贡献: 提出了一种可扩展的方法,通过自动标注…

【问题记录】使用命令语句从kaggle中下载数据集

从Kaggle中下载Tusimple数据集 1.服务器环境中安装kaggle 使用命令:pip install kaggle 2.复制下载API 具体命令如下: kaggle datasets download -d manideep1108/tusimple3.配置kaggle.json文件 如果直接使用命令会报错: root:~# kagg…

使用dbever连接 hsqldb

完整的url为 jdbc:hsqldb:hsql://ip:端口/别名 注意,hsqldb跟随应用启动和停止,所以当应用断点时,hsqldb也会连接不上导致查询数据失败,可以断点前进一步

IOS-相机权限申请-Swift

配置描述 在Info.plist文件中,新建一个键值对Privacy - Camera Usage Description(或者NSCameraUsageDescription),值为申请描述说明,自定义的 申请 然后在需要申请的文件中导入AVFoundation import AVFoundation…

时间序列预测 — BiLSTM-Attention实现单变量负荷预测(Tensorflow)

专栏链接:https://blog.csdn.net/qq_41921826/category_12495091.html 专栏内容 ​ 所有文章提供源代码、数据集、效果可视化 ​ 文章多次上领域内容榜、每日必看榜单、全站综合热榜 ​ ​ ​ ​ ​ ​ ​ 时间序列预测存在的问题 ​ 现有的大量方法没有真正的预测未…

高校学生选课系统源码开发方案

一、项目背景与目标 (一)项目背景 随着高校教育的发展,学生选课系统成为了高校管理中不可或缺的一部分。传统的手工选课方式存在着效率低下、易出错等问题,因此需要开发一款高效、便捷的高校学生选课系统。 (二&…

C++make_pair,你真的懂了吗?

其实写这篇文章我还是很忐忑的,因为用C也写了快一年了,平时代码量个人认为还可以,但是最近两天频繁犯错,下面先说说我写的错误吧! 我们都知道make_pair返回的是一个pair类型的函数,而pair这个键值对它又是…

MATLAB中simulink中scope同时显示两个输入信号

在使用scope时,需要两个输入信号的设置方法 1.点开scope图标 2 点击设置按钮, 然后弹出configuration properties:scope配置图,在Main选项下,在Number of input ports:1这里面更改数字,需要几…

【AI绘画】Midjourney到底是什么?看完就懂了!!!

手把手教你入门绘图超强的AI绘画,用户只需要输入一段图片的文字描述,即可生成精美的绘画。给大家带来了全新保姆级教程资料包 (文末可获取) 一、Midjourney 的原理 由 2022 年 3 月,美国一家工作室首次推出一款 AI 制…

Unity关于新手引导中实现遮罩镂空效果

在新手引导每一步中实现可以遮掉其他部分而显示当前需要点击的部分,只需要在每一步引导的时候设置对应的镂空区域的RectTransform.效果如下图: 代码: public class SelfMaskSet : MaskableGraphic, ICanvasRaycastFilter {[SerializeField]p…