SMPL源码解读

SMPL源码解读_第1张图片

这是源码的整体结构,先简单说一下各个文件里面是什么。

一、models文件

包含3个模型的.pkl文件,.pkl文件是python提供的可以以字符串的形式记录对象、变量的文件格式。这个模型里面包括了:

1.'J_regressor_prior':关节回归矩阵的先验,保存形式为CSC(用array保存的稀疏矩阵)

2.'f':面信息(三个顶点为一组表示面片的组成)

SMPL源码解读_第2张图片(不是全部,有省略)

3.'J_regressor':关节回归矩阵,保存形式CSC

4. 'kintree_table':关节树表

SMPL源码解读_第3张图片

5.'J':关节位置

SMPL源码解读_第4张图片

6.'weights_prior':蒙皮权重先验

7.'weights':蒙皮权重

8.'vert_sym_idxs' :顶点索引

9.'posedirs':姿势矫正

10.:姿势矫正蒙皮方式

11.'v_template':T pose 顶点信息

12.'':形状矫正

SMPL源码解读_第5张图片

 13.形状矫正蒙皮方式:

以上内容都有省略,但其实只需要知道有什么就行了,具体数字不重要(人眼也看不出意义),感兴趣可以用自己用下面代码写个脚本查看

SMPL源码解读_第6张图片

注释掉的是用来显示全部内容,并写到.txt里面,用的时候记得改文件路径

 

二、smpl_webuser

可以看到里面还有个文件夹hello_world,里面两个脚本hello_smpl.py和render_smpl.py,这是作者提供的示例代码,可用其生成自己设定shape、pose参数的模型,区别是hello_smpl.py是直接生成.obj文件,render_smpl.py是直接渲染在屏幕上;个人觉得用meshlab或者Blender查看.obj比较舒服,所以只介绍hello_smpl.py。

hello_smpl.py

SMPL源码解读_第7张图片

 可以看到很简单,先用写好的load_model方法,读出.pkl文件的内容,然后将里面的m.r(定点信息)和m.f(面信息)写进.obj,meshlab既可以根据这两个信息渲染出最终的模型。

所以重点放在其他几个脚本上,lbs.py, posemapper.py, serialization.py, verts.py,一共四个脚本

我根据正须(整体调用顺序)推一遍各个脚本和里面函数的作用。

1.serialization.py

脚本名可翻译为串行化,前面也提到这个名词,串行化,就是python用来将对象、变量转换成字符进行存储,所以里面是关于模型存储、读取的方法。

(1)存储模型

SMPL源码解读_第8张图片

 SMPL源码解读_第9张图片(attribute  n.属性)

SMPL源码解读_第10张图片

可以看到,就是检查model里面的内容,再写进新的文件里。

 

(2)命名规范检查

SMPL源码解读_第11张图片

 对模型的参数可能存在的不同的命名进行检查,并规范化,算是提高复用性

(3)模型初始化(更添加顶点信息 &更新 形状修正后的关节点信息)

SMPL源码解读_第12张图片

 在 if want_shapemodel:这一行之前,都是在加载,判断,添加修改 传入的数据,都是直接调用库里的函数。

SMPL源码解读_第13张图片

之后,通过将数据里的'shapedirs'(形状纠正)和'beta'(形状参数)点乘,加上'v_template'(T pose定点信息),得到形状修正后的定点信息'v_shaped'。

再用'J_regressor'和'v_shaped'计算出形状矫正后的新的关节位置'J'。

 'v_posed'的计算中调用了其他脚本的方法'posemap',等下再看

最终这个方法的作用是根据数据里的'posedirs'&'shapedirs'计算出'v_posed' & 'J',并更新\添加到原来的数据文件中。

(4)模型加载

SMPL源码解读_第14张图片首先调用方法(3)读取.pkl文件中的'v_posed' & 'J'以及其他信息,然后将.pkl中的参数按照名称建立成字典。verts_core是一个重载函数,主要作用是在蒙皮过程中模型空间和世界空间进行转换,返回值是顶点息和Jtr,具体之后分析。setattr将未进行坐标转换的定点信息作为属性k,添加到result。所以最后返回的加载模型得到的result包含了顶点的模型空间和世界空间内的坐标(疑问:其他信息呢?hello_smpl.py里明明还对。

2.posemapper.py

接下来先看在初始化模型中调用的posemap函数,已知这个是用来计算pose参数的

 SMPL源码解读_第15张图片

 

 有三个方法,但主要都是为第二个方法lortmin()服务,lortmin是一个很复杂的计算(没看懂),用到了罗德里斯公式,输出的是一个一维向量,这个向量与pose参数相乘后和posedirs矩阵点乘,这个脚本是用来将pose参数(旋转轴角,而且非线性)转换为线性可插值的旋转矩阵。

从纬度的计算看,

的结果是Nx3,从公式里可之'posedirs'也就是P的纬度是3Nx9K,''pose'向量在模型初始化的方法里初始化的3K,可以推导出'lortmin'的纬度是3x9x1,也符合旋转矩阵的表达形式(将3维的周角转换成3x3的旋转矩阵)。

3.verts.py

前面load_model中用到的重载函数verts_core(**args)也在该脚本调用。先看这个。

这个方法我这里只截图了命名,内容上也是根据已知的posedirs和shapedirs算出v_posed和J,其中的J的回归矩阵对是否为系数矩阵进行了判断,可能是为了防止有的模型定义变量名的不同。其他跟模型初始化的内容一样。作者给的注释是

其他地方也没有调用这个方法,应该是个模型定义的备选项

SMPL源码解读_第16张图片

 这里是被调用的重载函数的定义,假定了蒙皮形式为线性蒙皮和参数状态。

整个脚本就是先假定一下smpl的T pose 和 J 和 蒙皮形式。
 

4.lbs.py

(1)将模型从模型空间转变到世界空间

 通过对kintree_table的按列读取,确定每个关节的parent

SMPL源码解读_第17张图片

 从kintree_table按列看,即可得知关节之间的父子关系

SMPL源码解读_第18张图片

 

SMPL源码解读_第19张图片

 lambda 是方法的简写,冒号前为变量,后面为函数体,.vstack()是按列方向排列,result[0]是通过罗德里斯公式,得到0关节在世界坐标系的位置。因为0关节没有parent,其世界坐标和模型坐标是一样的。所以没有运算,只是通过行列排列即可得到。

SMPL源码解读_第20张图片

for循环对每个关节,通过点承消去parent对自身坐标的改变,得到关节在世界坐标中的位置。

排列后得到所有关节世界位置组成的矩阵result_global。

SMPL源码解读_第21张图片

 再将世界坐标逐个点乘parent的坐标得到关节在模型坐标中的位置。返回最终结果

(2)关节在蒙皮中对顶点的影响

SMPL源码解读_第22张图片

 

你可能感兴趣的:(深度学习,人工智能)