opencv相机标定和人头姿态估计案例

前言

头部驱动除了之前关注的表情驱动外,还有眼球驱动和头部方向驱动。本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向。

国际惯例,参考博客:

opencv:Camera Calibration and 3D Reconstruction

opencv:Real Time pose estimation of a textured object

cv.solvePnP位姿估计旋转向量精度分析

头部姿态估计原理及可视化

重磅!头部姿态估计「原理详解 + 实战代码」来啦!

相机矩阵(Camera Matrix)

Python cv2.decomposeProjectionMatrix方法代码示例

face_landmark

head-pose-estimation

Face-Yaw-Roll-Pitch-from-Pose-Estimation-using-OpenCV

talking-head-anime-demo

OpenVtuber

相机标定理论

几种坐标系

先看从opencv官网中扒下来的两幅图,代表针孔相机模型(pinhole camera model)

在这里插入图片描述

其中涉及到几种坐标系:

  • 世界坐标系:一个固定不变的坐标系,原点通常固定不变,右图的www坐标系
  • 相机坐标系:相机在世界坐标系下的姿态,右图的ccc坐标系
  • 图像坐标系:成像平面,图中的x-y坐标轴,其原点是相机的光轴与成像平面的胶垫
  • 像素坐标系:最终图像,图中的u-v坐标轴,原点在左上角,就跟opencv输出的图片一样,左上角代表(0,0)(0,0)(0,0)像素位置。

图像坐标系和像素坐标系横轴和纵轴方向一致,但是单位不同,一个是物理单位,一个是像素单位,一般有一个对应的缩放关系,代表一个像素在成像平面上的大小。

针孔相机的目标就是把3D坐标点PwP_wPw利用透视变换(perspective transformation)投影到图像平面上,得到对应像素ppp。其中PwP_wPwppp都在齐次坐标系下表示。

无畸变情况下,针孔相机的投影变换可以表示为:
sp=A[R∣t]Pws\ p=A[R|t]P_w s p=A[Rt]Pw
其中PwP_wPw为世界坐标系下的3D坐标点,ppp是图像平面上的2D像素点,AAA是相机内参矩阵,RRRttt分别描述了世界坐标系到相机坐标系的旋转和平移变换,sss是任意尺度的投影变换(非相机模型的参数,其实就是图像坐标系到像素坐标系的变换系数)。

世界坐标系到相机坐标系

旋转-平移矩阵[R∣t][R|t][Rt]是投影变换(projective transformation)和齐次变换(homogeneous transformation)的乘积。

维度为(3,4)(3,4)(3,4)投影变换可以将相机坐标系里的3D坐标映射到成像平面的2D坐标,并且在归一化的相机坐标系x′=XcZcx'=\frac{X_c}{Z_c}x=ZcXcy′=YcZcy'=\frac{Y_c}{Z_c}y=ZcYc下表示出来
Zc[x′y′1]=[100001000010][XcYcZc1]Z_c\begin{bmatrix} x'\\ y'\\1 \end{bmatrix}=\begin{bmatrix} 1&0&0&0\\ 0&1&0&0\\ 0&0&1&0 \end{bmatrix}\begin{bmatrix} X_c\\Y_c\\Z_c\\1 \end{bmatrix} Zcxy1=100010001000XcYcZc1
而齐次变换通常在相机外参RRRttt中体现出来,代表世界坐标系到相机坐标系的变换,因此给定一个世界坐标系下的点PwP_wPw,那么相机坐标系下的对应点为:
Pc=[Rt01]PwP_c=\begin{bmatrix} R&t\\0&1 \end{bmatrix}P_w Pc=[R0t1]Pw
这个齐次变换一般就是由一个(3,3)的旋转矩阵和一个(3,1)的平移向量组成:
[Rt01]=[r11r12r13txr21r22r23tyr31r32r33tz0001]\begin{bmatrix} R&t\\0&1 \end{bmatrix}=\begin{bmatrix} r_{11}&r_{12}&r_{13}&t_x\\ r_{21}&r_{22}&r_{23}&t_y\\ r_{31}&r_{32}&r_{33}&t_z\\ 0&0&0&1 \end{bmatrix} [R0t1]=r11r21r310r12r22r320r13r23r330txtytz1
因此
[XcYcZc1]=[r11r12r13txr21r22r23tyr31r32r33tz0001][XwYwZw1]\begin{bmatrix} X_c\\Y_c\\Z_c\\1 \end{bmatrix}=\begin{bmatrix} r_{11}&r_{12}&r_{13}&t_x\\ r_{21}&r_{22}&r_{23}&t_y\\ r_{31}&r_{32}&r_{33}&t_z\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} X_w\\Y_w\\Z_w\\1 \end{bmatrix} XcYcZc1=r11r21r310r12r22r320r13r23r330txtytz1XwYwZw1
结合投影变换和齐次变换,就可以得到将世界坐标系下3D点映射到归一化相机坐标系下的成像平面下2D点的变换:
Zc[x′y′1]=[R∣t][XwYwZw1]=[r11r12r13txr21r22r23tyr31r32r33tz][XwYwZw1]Z_c\begin{bmatrix} x'\\y'\\1 \end{bmatrix}=[R|t]\begin{bmatrix} X_w\\Y_w\\Z_w\\1 \end{bmatrix}=\begin{bmatrix} r_{11}&r_{12}&r_{13}&t_x\\ r_{21}&r_{22}&r_{23}&t_y\\ r_{31}&r_{32}&r_{33}&t_z \end{bmatrix}\begin{bmatrix} X_w\\Y_w\\Z_w\\1 \end{bmatrix} Zcxy1=[Rt]XwYwZw1=r11r21r31r12r22r32r13r23r33txtytzXwYwZw1
其中x′=XcZcx'=\frac{X_c}{Z_c}x=ZcXcy′=YcZcy'=\frac{Y_c}{Z_c}y=ZcYc

相机坐标系到像素坐标系

相机内参矩阵AAA通常用K表示,用于将相机坐标系下的3D坐标点投影到像素坐标系中。
p=APcp=AP_c p=APc
通常相机内参矩阵AAA包含了以像素为单位的焦距fxf_xfxfyf_yfy,以及靠近图像中心的原点(cx,cy)(c_x,c_y)(cx,cy)
A=[fx0cx0fycy001]A= \begin{bmatrix} f_x & 0 & c_x \\ 0&f_y&c_y\\ 0&0&1 \end{bmatrix} A=fx000fy0cxcy1
所以
s[uv1]=[fx0cx0fycy001][XcYcZc]s\begin{bmatrix} u\\v\\1 \end{bmatrix}=\begin{bmatrix} f_x & 0 & c_x \\ 0&f_y&c_y\\ 0&0&1 \end{bmatrix}\begin{bmatrix} X_c\\Y_c\\Z_c \end{bmatrix} suv1=fx000fy0cxcy1XcYcZc
相机内参,顾名思义只与相机自身有关,与外部环境无关,所以一次标定以后,只要你不动焦距,就可以永久使用。

总结:世界坐标系到像素坐标系

将内外参矩阵放在一起就能把sp=A[R∣t]Pws\ p=A[R|t]P_ws p=A[Rt]Pw重写成:
s[uv1]=[fx0cx0fycy001][r11r12r13txr21r22r23tyr31r32r33tz][XwYwZw1]s\begin{bmatrix} u\\v\\1 \end{bmatrix}=\begin{bmatrix} f_x&0&c_x\\ 0&f_y&c_y\\ 0&0&1\\ \end{bmatrix}\begin{bmatrix} r_{11}&r_{12}&r_{13}&t_x\\ r_{21}&r_{22}&r_{23}&t_y\\ r_{31}&r_{32}&r_{33}&t_z \end{bmatrix}\begin{bmatrix} X_w\\Y_w\\Z_w\\1 \end{bmatrix} suv1=fx000fy0cxcy1r11r21r31r12r22r32r13r23r33txtytzXwYwZw1
如果Zc≠0Z_c\neq0Zc=0,那么
[uv]=[fxXc/Zc+cxfyYc/Zc+cy]\begin{bmatrix} u\\v \end{bmatrix}= \begin{bmatrix} f_xX_c/Z_c+c_x\\ f_yY_c/Z_c+c_y \end{bmatrix} [uv]=[fxXc/Zc+cxfyYc/Zc+cy]
其中
[XcYcZc]=[R∣t][XwYwZw1]\begin{bmatrix} X_c\\Y_c\\Z_c \end{bmatrix}=[R|t]\begin{bmatrix} X_w\\Y_w\\Z_w\\1 \end{bmatrix} XcYcZc=[Rt]XwYwZw1
就得到最开始描述的左图中的u-v坐标系映射模型了。

【注】上述理论是基于畸变参数为0的情况下,关于不为零的时候,请自行查阅opencv官方文档描述或者其他资料。

头部姿态估计

理论

通过内外参矩阵可以将世界坐标系下的3维点映射到成像平面,那么同理,可以利用相机内参、世界坐标系的3D点、成像平面的2D点,找到世界坐标系到相机坐标系的旋转和平移(外参矩阵)。

在做头部姿态估计的时候,我们仅仅知道人脸关键点,其它信息一无所知,那么应该怎么求解呢?

通过后五篇参考博客的源码分析,大致流程就是:

  • 建立一个虚假的3D头模,找到几个人脸关键点的3D坐标
  • 假定当前相机的内参矩阵和畸变系数
  • 利用solvePnP求解平移向量和旋转向量
  • 利用decomposeProjectionMatrix将旋转向量转换为欧拉角

其中solvePnP的函数描述如下:

retval, rvec, tvec	=	cv.solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs[, rvec[, tvec[, useExtrinsicGuess[, flags]]]]	)

输入参数:

  • objectPoints:世界坐标系下的3D坐标
  • imagePoints:2D投影坐标
  • cameraMatrix:相机内参矩阵
  • distCoeffs:畸变系数

输出:

  • rvec:旋转向量,可使用Rodrigues转换为旋转矩阵
  • tvec:平移向量

其中decomposeProjectionMatrix的函数描述如下:

cameraMatrix, rotMatrix, transVect, rotMatrixX, rotMatrixY, rotMatrixZ, eulerAngles	=	cv.decomposeProjectionMatrix(	projMatrix[, cameraMatrix[, rotMatrix[, transVect[, rotMatrixX[, rotMatrixY[, rotMatrixZ[, eulerAngles]]]]]]]	)

输入参数:

  • projMatrix:维度为(3,4)(3,4)(3,4)的投影矩阵PPP

返回参数:

  • cameraMatrix:内参矩阵
  • rotMatrix:外部旋转矩阵
  • transVect:外部平移矩阵
  • rotMatrixX:绕x轴旋转的矩阵
  • rotMatrixY:绕y轴旋转的矩阵
  • rotMatrixZ:绕z轴旋转的矩阵
  • eulerAngles:旋转欧拉角

实现

例如最后一个参考博客中的源码解析分别为:

  • 预加载3D人脸关键点模型

    首先预加载一个3D人脸关键点模型,源码提供了人脸的39个关键点,我提取了其中12个关键点,关键点坐标如下:

    array([[ 29.64766 ,  10.      ,  66.01275 ],[126.870285,  10.      ,  66.01275 ],[ 60.359673,  34.85047 ,  44.13414 ],[ 25.144653,  33.933437,  39.87654 ],[ 96.15827 ,  34.85047 ,  44.13414 ],[131.37329 ,  33.933437,  39.87654 ],[ 78.25897 ,  88.78672 ,  67.6343  ],[ 50.51882 , 109.59447 ,  50.48531 ],[ 78.25897 , 105.25116 ,  67.04956 ],[105.99912 , 109.59447 ,  50.48531 ],[ 78.25897 , 119.950806,  60.976673],[ 78.25897 , 162.94363 ,  40.70434 ]], dtype=float32)
    

    原始39个关键点与对应提取的12个关键点在2D图像上的位置关系如下:

    在这里插入图片描述

  • 提取真实人脸关键点

    利用opencv或者HRNet模型,提取真实图像中的2D人脸关键点,可参考之前换脸的博客,或者去我github上找源码也可以,效果如下:

    在这里插入图片描述

  • 计算朝向
    首先创建内参矩阵:

    H,W = img.shape[0],img.shape[1]
    matrix = np.array([[W,0,W/2.0],[0,W,H/2.0],[0,0,1]])
    

    然后求解外参矩阵,调用solvPnP函数求解旋转向量和平移向量

    _,rot_vec,trans_vec = cv2.solvePnP(obj[pick_model,...].astype("float32"),points[pick_dlib,...].astype("float32"),matrix,None,flags=cv2.SOLVEPNP_DLS)
    

    将旋转向量和平移向量组合成外参矩阵的形式

    rot_mat = cv2.Rodrigues(rot_vec)[0]
    pose_mat = cv2.hconcat((rot_mat, trans_vec))
    

    最后将旋转向量转换为欧拉角:

    
    euler_angle = cv2.decomposeProjectionMatrix(pose_mat)[-1]
    

    可视化效果如下:

在这里插入图片描述

后记

结果有时候受到你初始模型的影响,所以建议多找些源码测试一下,找到一个合适的3D模型使用。而且在驱动卡通角色时候,由于建模和游戏引擎的原因,坐标系可能不同,因而欧拉角也要做适当的变换,比如我的项目基于python和unity交互的卡通角色肢体和表情驱动(深度学习)中关于表情驱动部分的实验。

完整的python实现放在微信公众号的简介中描述的github中,有兴趣可以去找找。同时文章也同步到微信公众号中,有疑问或者兴趣欢迎公众号私信。

在这里插入图片描述

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

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

相关文章

卡通角色表情驱动系列二

前言 之前介绍了使用传统算法求解BS系数的表情驱动方法,其中提到过的三种方法之一是基于网格形变迁移做的,那么这篇文章就是对《Deformation Transfer for Triangle Meshes》做表情驱动的解析。 国际惯例,参考博客: 论文原文《…

UE自带重定向原理

UE自带重定向方法验证 核心源码在VS的解决方案中的位置: UE4\Source\Developer\AssetTools\Private\AssetTypeActions\AnimSequence.cpp中第3237行RemapTracksToNewSkeleton函数 跳转方法 AssetTypeActions_AnimationAsset.cpp的RetargetNonSkeletonAnimationHa…

【caffe-Windows】caffe+VS2013+Windows无GPU快速配置教程

前言 首先来一波地址: happynear大神的第三方caffe:http://blog.csdn.net/happynear/article/details/45372231 Neil Z大神的第三方caffe:https://initialneil.wordpress.com/2015/01/11/build-caffe-in-windows-with-visual-studio-2013-…

【caffe-Windows】caffe+VS2013+Windows+GPU配置+cifar使用

前言 国际惯例,先来波地址: CUDA WIN7:链接:http://pan.baidu.com/s/1nvyA3Qp 密码:h0f3 官方网址:https://developer.nvidia.com/cuda-toolkit CUDA WIN10:链接:http://pan.baidu.com/s/1…

【一些网站的收集】包含机器学习深度学习大牛主页等

数学概念部分 旋转矩阵、欧拉角、四元数的比较 欧拉角和四元数的表示 四元数与旋转 B样条曲线 非常好的概率统计学习的主页 误差方差偏差 编程语言学习 C#编程视频 OpenGL编程NeHe OpenGL官网 OpenGL“我叫MT“纯手工3D动画制作之1——基础介绍 【强大】非常好的Op…

Eureka源码分析

Eureka源码分析 Eureka server入口: Spring.factories PS: 意味着如果加载EurekaServerAutoConfiguration成功,需要 ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)需加载成功. 通过Bean注入了很多类 本质上, eureka-server包含很多事件: EurekaInstanceC…

matlab程序中,如何解决矢量长度必须相同的问题

主要原因就是画图的x和y长度不一样,我用一个例子说明。 问题代码: clear all;close all;clc;x 0 : 1: 9;y sin(x);n 2*length(x);yi interpft(y, n);xi 0 : 0.5 : 10;hold on ;plot(x, y ,ro);plot(xi, yi, b.-);plot(x, sin(x),m--);legend(原始…

matlab 功率谱分析函数psd用法

psd简介 PSD(power spectrum analysis)功率谱分析,PSD在给定频带上的积分计算信号在该频带上的平均功率。与均值-平方谱相反,这个光谱中的峰值并没有反映出给定频率的能量。 单边PSD包含了信号的总功率在频率间隔从DC到一半的奈奎斯特速率。双侧PSD包含…

linux tar (打包、压缩、解压)命令

打包程序:tar c: 创建文档t: 列出存档内容x:提取存档f: filename 要操作的文档名v:详细信息 一:打包 打包:是指把文件整合在一起,不压缩 1.将文件打包:tar cf a.tar…

虚拟机添加硬盘扩容

1.设置→添加→硬盘 2.选择磁盘类型 3.开启虚拟机 4.用ls 命令查看:ls /dev/sd* 5.最后就可以对sdb进行分区操作 这里好麻烦,等我有空,在补上! . . .

利用matlab将三维数据画成三维立体图

首先先分析对象。将数据利用matlab画出图,最开始是导入数据,然后处理数据,最后将处理的数据画出来。 所以我将它分为三个步骤。 第一步:导入数据 如果是mat数据。可以直接load如果是txt数据。可以用txtread如果是excel数据。可…

世界坐标、相机坐标、图像坐标、像素坐标的原理、关系,并用matlab仿真

世界坐标、相机坐标、图像坐标、像素坐标的原理、关系,并用matlab仿真 照相机是日常生活中最常见的。它能把三维的空间图片等比例缩小投影在照片上,称为一个二维图像。 以下我们就讲一讲原理,并相应的进行matlab仿真。 在学之前&#xff0…

matlab 三维高程根据图片颜色给对应点赋予颜色

目录 1. 问题分析 2. 技术分析 3. 程序代码 4. 代码运行结果 1. 问题分析 日常工作尤其是测绘、地质、遥感行业,需要画DEM模型,并在这个模型的基础上,进行着色、渲染。比如,地质分析地面三维地表形变之时,需要根据D…

matlab 计算N天前(后)的日期

注意时间的格式:是字符串、数字还是日期? 下面是计算明天、今天、昨天的日期。 day1 datetime(datestr(now,yyyy-mm-dd))caldays(1)%tomorrowday0 datetime(datestr(now,yyyy-mm-dd))%todayday_1 datetime(datestr(now,yyyy-mm-dd))-caldays(1)%yest…

CAT arguments dimensions are not consistent.CAT参数的维度不一致。

错误实例: 在写符号矩阵的时候常常会出现下面错误: 错误:CAT arguments dimensions are not consistent. CAT参数的维度不一致。 举个栗子: clear; close all; clc; syms A_0 B_0 B C D E F G H ;T_3 [2*A_0 C-D*1i H G*1i;C…

传感器尺寸、像素、DPI分辨率、英寸、毫米的关系

虽然网上有很多这种资料,但是太过于复杂,每个人的说法都不一样,看的让人云里雾里的,我总结了一下,不知道对不对! 1. 1英寸25.4mm 2. 传感器尺寸:传感器的尺寸是指传感器的大小,一般…

利用PS将n张图制作成动态GIF图

第一步:打开PS,导入图片,文件→脚本→将文件载入堆栈… 数据量大的话,就耐心等待一下。 第二步: 创建祯动画 如果没有这个,可以点击窗口→时间轴 如果祯排列顺序反了,这样 最后按照自己要求设置祯动画时间&…

matlab padarray函数详解

本文来自于matlab帮助页面,命令:help padarray 语法: B padarray(A,padsize) B padarray(A,padsize,padval) B padarray(A,padsize,padval,direction) gpuarrayB padarray(gpuarrayA,___) 描述: B padarray(A,padsize…

matlab 三维画图函数错误提示:数据维度必须一致

用三维画图软件经常会出现下面错误 以mesh(x,y,z)为例: 主要原因是因为没有注意Z数据格式,Z必须是矩阵形式。而且Z矩阵的m*n必须与y,x相关, mesh(X,Y,Z) 使用 Z 确定的颜色绘制线框网格,因此其颜色与曲面高度成比例。如果 X 和 …

毕业论文格式修改方法

好久没更新博客,忙着毕业论文。刚答完辩时就想写一篇关于论文格式的博客,这样在修改论文的时候很轻巧很多,别人花一两天修改好,我只需要几个小时(包括图片大小、页面调整、错别字修改)。话不多说&#xff0…