3D姿态估计——ThreeDPose项目简单易用的模型解析

前言

之前写过tensorflow官方的posenet模型解析,用起来比较简单,但是缺点是只有2D关键点,本着易用性的原则,当然要再来个简单易用的3D姿态估计。偶然看见了ThreeDPose的项目,感觉很强大的,所以把模型扒下来记录一下调用方法。

参考博客:

ThreeDPose官方代码

微软的ONNX模型解析库

ONNX解析库的pythonAPI文档

3D姿态估计最大的好处就是卡通角色的肢体驱动了,其实就是单目摄像头的动捕方法。

在这里插入图片描述

理论和代码解析

可以从官方去下载模型,戳这里,或者在文末的百度网盘下载。

模型结构

模型已经被作者转换成ONNX的模型文件了,所以下载netron软件去可视化模型,打开以后可以发现模型的网络结构和输入输出

在这里插入图片描述

这里说明一下各部分含义:

有三个input,其实都是一样,都要输入(448,448,3)的图片

有四个output,解析模型即提取关键点的时候,只需要第3和4个输出,具体解析方法看下面代码解析。

代码解析

windows上为了读取ONNX的模型文件,可以使用微软提供的onnxruntime这个库,直接pip安装即可,这个库的文档见上面参考博客的2和3。

先引入相关的库文件

import numpy as np
import onnxruntime as rt
import cv2import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

首先就是加载模型,输入图片,推断当前图片的四个输出

#加载模型
sess = rt.InferenceSession("Resnet34_3inputs_448x448_20200609.onnx")
inputs = sess.get_inputs()
#读取图片
img = cv2.imread("D:/photo/pose/5.jpg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
img = cv2.resize(img,(448,448))
img = img.astype(np.float32)/255.0
img = img.transpose(2,1,0)
img = img[np.newaxis,...]
img.shape
#输入到网络结构中
pred_onx = sess.run(None,{inputs[0].name:img,inputs[1].name:img,inputs[2].name:img
})

获取输出

offset3D = np.squeeze(pred_onx[2])
heatMap3D = np.squeeze(pred_onx[3])
print(offset3D.shape)#(2016, 28, 28)
print(heatMap3D.shape)#(672, 28, 28)
print(offset3D.shape[0]/heatMap3D.shape[0])#3.0

按照参考博客1的unity代码解析输出,可以发现这个命名和posenet的解析一模一样,使用heatmap粗略定位关节位置,然后使用offsetheatmap结果上精确调整关节位置。

heatmap(672,28,28)(672,28,28)(672,28,28) 代表 24个关节的28个大小为(28,28)(28,28)(28,28)的特征图。而offsetheatmap的特征图多三倍,很明显就是刚才说的精确定位,只不过需要在offset中定位到x,y,z三个坐标,所以就是三倍关系了。

定位原理就是:heatmap相当于把原图划分为(28,28)(28,28)(28,28)的网格点,每个网格点代表附近有一个关节的概率,通过找到某个关节最可能在heatmap哪一副特征图的哪个网格,我们根据heatmap的索引在offset中找到对应的3个精确矫正xyz坐标的特征图,这三个特征图的值就是对应关节的xyz坐标相对于当前heatmap网格位置的精确偏移量。

在写代码之前,着重关注一下heatmapoffset的特征图相对于关节是一个怎样的顺序。

  • heatmap的顺序是第1个关节的第1个特征图、第1个关节的第2个特征图、…、第2个关节的第1个特征图、第二个关节的第2个特征图、…、第24个关节的第28个特征图
  • offsetmap的顺序是第1个关节的第1个特征图对应的x坐标偏移、第1个关节的第2个特征图对应的x坐标偏移、第1个关节的第3个特征图对应的x坐标偏移、…、第1个关节的第28个特征图对应的x坐标偏移、…、第2个关节的第1个特征图对应的x坐标偏移、…、第24个关节的第28个特征图对应的x坐标偏移、第1个关节的第1个特征图对应的y坐标偏移、第1个关节的第2个特征图对应的y坐标偏移、…、第24个关节的第28个特征图对应的y坐标偏移、第1个关节的第1个特征图对应的z坐标偏移、第1个关节的第2个特征图对应的z坐标偏移、…、第24个关节的第28个特征图对应的z坐标偏移。

说了一堆,不如看代码简单明了:

比如提取第j个关节的3D坐标位置,对应的特征图是[j∗28,(j+1)∗28−1][j*28, (j+1)*28-1][j28,(j+1)281] ,然后从这里面找到最大值的位置就是当前关节最可能在哪个特征图的哪个网格位置上。

# 找到第j个关节的28个特征图,并找到最大值的索引
joint_heat = heatMap3D[j*28:(j+1)*28,...]
[x,y,z] = np.where(joint_heat==np.max(joint_heat))
# 避免有多个最大值,所以取最后一组
x=int(x[-1])
y=int(y[-1])
z=int(z[-1])

然后按照找到的heatmap索引去查询offset,找到精确的矫正值

pos_x = offset3D[j*28+x,y,z] + x
pos_y = offset3D[24*28+j*28+x,y,z] + y
pos_z = offset3D[24*28*2+j*28+x,y,z] + z

每个坐标都是在当前网格的位置上加上精确的偏移量,因为xyz分别在offset上分别隔了24∗2824*282428个特征图,所以出现了y和z的第一个索引有变化。

如果还有疑问,建议去看看前面的2D姿态估计的解析,然后自己手写一遍解析算法就理解了。

把所有关节的位置存到kps然后可视化看看

%matplotlib inlinefig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter3D(kps[:,0],-kps[:,1],-kps[:,2],'red')
parent = np.array([0,1,2,3,3,  1,6,7,8,8,   12,15,14,15,24,  24,16,17,18,  24,20,21,22, 0])-1;
for i in range(24):if(parent[i]!=-1):ax.plot3D(kps[[i,parent[i]],0], -kps[[i,parent[i]],1], -kps[[i,parent[i]],2], 'gray')ax.xaxis.set_tick_params(labelsize=10)
ax.yaxis.set_tick_params(labelsize=10)
ax.zaxis.set_tick_params(labelsize=10)ax.view_init(elev=10., azim=180)

在这里插入图片描述

其实把关键点保存下来用matlab画更好看

%test
clear;clc;close all
% a=dlmread("D:\code\python\ThreeDPose\unity_data\kps100.txt");
a=[0.292807,14.994286,6.560671;
-0.123055,15.480020,8.367174;
-2.666008,19.526012,9.689341;
-3.994198,19.631011,9.902868;
-3.537779,20.058028,9.978683;
1.467720,11.799177,6.442903;
-1.372830,10.170244,5.793396;
-2.116019,9.653587,3.315798;
-1.332626,9.906492,1.420080;
-2.152191,9.461523,2.985400;
0.378545,12.237494,4.861950;
-0.739344,12.452946,4.971550;
-0.678598,14.203241,5.388392;
-0.671332,13.130285,4.690326;
-1.256000,12.918788,5.623796;
0.216525,13.818989,12.736559;
0.793380,13.383041,17.217848;
-0.301103,13.578390,22.771406;
-0.406112,13.623824,23.280164;
0.380849,11.442492,12.643937;
-2.110893,11.706800,18.199155;
-1.205901,12.321800,22.871755;
-2.116101,12.384523,24.476315;
-0.500211,12.706436,11.490892];parent =[15,1,2,3,3,  15,6,7,8,8,   12,15,14,15,24,  24,16,17,18,  24,20,21,22, 0];
plot3(a(:,1),-a(:,2),-a(:,3),'ro','MarkerSize',2,'MarkerFaceColor','r')
for i=1:24if(parent(i)~=0)line([a(i,1) a(parent(i),1)],[-a(i,2) -a(parent(i),2)],[-a(i,3) -a(parent(i),3)])end
end
axis equal
axis off

在这里插入图片描述

24个关节的名称分别标注一下说一下吧,从unity代码中的VNectModel.cs能找到,玩过骨骼动画的基本知道每个单词的代表的关节,这里就不画骨骼结构图了,自己对应到上面的关键点图中即可。

rShldrBend,    rForearmBend,    rHand,    rThumb2,    rMid1,
lShldrBend,    lForearmBend,    lHand,    lThumb2,    lMid1,
lEar,    lEye,    rEar,    rEye,    Nose,
rThighBend,    rShin,    rFoot,    rToe,
lThighBend,    lShin,    lFoot,    lToe,
abdomenUpper,

后记

其实如果要做肢体驱动,还需要

  • 根据上述关节计算额外的一些关节坐标,这一块不在本博文的学习范围内,博文只关注怎么简单的使用这个模型去解析关节位置。
  • 将关节坐标映射到原图,这个根据比例原图比例缩放一下即可,与posenet一样,也不做阐述。

模型文件网盘地址:
链接:https://pan.baidu.com/s/1TsvALWJRIoCAtQ9ffcno7w
提取码:rfow

本博文同步更新到微信公众号中,有兴趣可关注一波,代码在微信公众号简介的github找得到,有问题直接公众号私信。

在这里插入图片描述

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

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

相关文章

简易的素描图片转换流程与实现

前言 之前经常在网上看到用PS实现真实图片到素描图片的转换,但是流程都大同小异,身为一只程序猿,必须来个一键转化额。 国际惯例,参考博客: Photoshop基础教程:混合模式原理篇 颜色减淡的原理讲解以及应…

一个简单好用的磨皮祛斑算法理论和python实现

前言 最近看了一个磨皮算法祛斑感觉效果不错,效果图看文末就行,个人觉得效果非常不错滴。 国际惯例,参考博客: 磨皮算法的源码:YUCIHighPassSkinSmoothing How To Smooth And Soften Skin With Photoshop 图像算法…

OpenVINO——配置与道路分割案例

前言 最近看到了一个深度学习库OpenVINO,专门用于Intel硬件上部署深度学习模型,其内置了非常非常多使用的预训练模型,比如道路分割、人脸提取、3D姿态估计等等。但是配置和调用有点小恶心,这里以道路分割为例,展示如何…

图像颜色迁移《color transfer between images》

前言 前段时间,在深度学习领域不是有个比较火的方向叫风格迁移的嘛,对于我这种不喜欢深度学习那种不稳定结果的人来说,还是想看看传统图像处理领域有什么类似的技术,发现了一个颜色迁移的算法,很久前的论文了。 国际…

ColorSpace颜色空间简介

前言 如果看过之前的介绍的图像颜色迁移《color transfer between images》和颜色协调模型Color Harmoniztion就会发现,大部分图像处理算法虽然输入输出是RGB像素值,但是中间进行算法处理时很少直接更改RGB值,而是转换到其它空间&#xff0c…

Ogre共享骨骼与两种骨骼驱动方法

前言 最近业务中用到Ogre做基于3D关键点虚拟角色骨骼驱动,但是遇到两个问题: 身体、头、眼睛、衣服等mesh的骨骼是分开的,但是骨骼结构都是一样的,需要设置共享骨骼驱动的时候可以直接修改骨骼旋转量,或者将旋转量存…

仿射变换和透视变换

前言 在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐,保证一张人脸贴到另一张人脸时,大小完全一致;所以有必要理解一下这两个概念的区别,由于以实用性为目的,所以所有的图像算…

obj格式解析

前言 最近处理一些网格渲染的时候,需要解析Obj文件,从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例,参考博客: 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…

Flask服务部署与简单内网穿透

前言 最近学习部署的时候,想到深度学习里面通常用的部署方法是flask做服务端,然后使用nginx做负载均衡,貌似也能做内网穿透。不过我不太懂负载均衡,只想利用本地电脑搭建一个简单的服务器,实现外部调用API服务的功能。…

OpenCV学习——轮廓检测

前言 轮廓检测是传统视觉中非常常用的功能,这里简单记录一下opencv中的轮廓检测算法使用方法,至于理论,后续有机会再去细品。 国际惯例: OpenCV官方的轮廓检测教程python版 OpenCV中的二值化方法教程 OpenCV轮廓层级官方文档…

RBF神经网络理论与实现

前言 最近发现有挺多人喜欢径向基函数(Radial Basis Function,RBF)神经网络,其实它就是将RBF作为神经网络层间的一种连接方式而已。这里做一个简单的描述和找了个代码解读。 之前也写过一篇,不过排版不好看,可以戳这里跳转 国际惯例&#x…

基于python和unity交互的卡通角色肢体和表情驱动(深度学习)

前言 最近看到了好多卡通角色的肢体驱动的东东,感觉是时候发挥一下读研时候学的东西了,而且虽然现在不炼丹了,但是还是得保持吃丹的技能。这个项目找了很多很多代码进行测试,最终集成了一个3D姿态估计和人脸关键点提取的代码。 …

OpenCV学习——形态学

前言 继续学习图像里面的形态学知识——结构元、腐蚀、膨胀、开运算、闭运算、击中/不击中变换。以及部分基本形态学算法,包括边界提取、空洞填充、连通分量的提取、凸壳、细化、粗化、骨架、裁剪、形态学重建。 其实就是对冈萨雷斯的《数字图像处理》中第9章节《…

径向基函数RBF三维网格变形

前言 之前写过径向基函数(RBF)神经网络做分类或者拟合。然后挖了个坑说在《Phase-Functioned Neural Networks for Character Control》里面提到了用于做地形编辑,所以这篇博客就是解析一下如何用RBF做网格编辑系统。 参考博客: Noe’s tutorial on d…

OBJ可视化——UV还原(修正)

前言 前面写过一篇obj格式解析的博客,但是这篇文章中可视化的工作是参考PRNet的源码进行的,后来细细思考了一下,有点问题,具体看下面。 问题来源 在PRNet源码的render.py中有个函数render_texture,是作者用于将uv展…

Unity中BVH骨骼动画驱动的可视化理论与实现

前言 找了很久使用BVH到unity中驱动骨骼动画的代码,但是都不是特别好用,自己以前写过,原理很简单,这里记录一下。 理论 初始姿态 在BVH或者其它骨骼动画中,一般涉及到三种姿势:A-pose,T-pos…

卡通驱动项目ThreeDPoseTracker——模型驱动解析

前言 之前解析过ThreeDPoseTracker这个项目中的深度学习模型,公众号有兄弟私信一些问题,我刚好对这个项目实现有兴趣,就分析一波源码,顺便把问题解答一下。 这个源码其实包括很多内容:3D姿态估计,坐标平滑…

卡通驱动项目ThreeDPoseTracker——关键点平滑方案解析

前言 之前对ThreeDPoseTracker的深度学习模型和unity中的驱动方法进行过解析,还有一个比较重要的就是从深度学习模型出来的3D关键点数据会有抖动,在ThreeDPoseTracker源码中有做两次平滑,一部分是卡尔曼滤波,还有一部分是低通滤波…

卡通角色表情驱动系列一

前言 分析完ThreeDPoseTracker来做卡通角色的身体驱动,接下来在卡通驱动领域还有一个是表情驱动。对这个真的是一窍不通啊,只能慢慢看论文了。 国际惯例,参考博客/论文: 《Landmark-guided deformation transfer of template f…

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

前言 头部驱动除了之前关注的表情驱动外,还有眼球驱动和头部方向驱动。本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向。 国际惯例,参考博客: opencv:Camera Calibration and 3D Reconstruction opencv:…