• ThreeDPoseTracker项目解析


    目录

    一、源码

    二、肢体驱动

    1.算法模型

    (1)数据说明

    (2)算法理论解析

    2.Unity肢体驱动

    (1)相关知识

    (2)项目中的使用


    一、源码

            Digital Standard Co., LTD. (github.com)

    参考文章:

    插件:ThreeDPoseTracker源码相关插件

    二、肢体驱动

    1.算法模型

    (1)数据说明

            项目中算法模型预测数据的代码主要为VNectBarracudaRunner.cs。函数PredictPose()中更新的节点数据为JointPoint中的三个变量(也就是驱动时要用到的算法数据):

    1. public class JointPoint
    2. {
    3. public Vector3 Pos3D = new Vector3();
    4. public float score3D;
    5. public bool Visibled;
    6. }

            JointPoint类中还有几个变量是预测过程中用于数据优化(防抖动等)的:

    1. public class JointPoint
    2. {
    3. public Vector3 Now3D = new Vector3();
    4. public Vector3[] PrevPos3D = new Vector3[6];
    5. public Vector3 P = new Vector3();
    6. public Vector3 X = new Vector3();
    7. public Vector3 K = new Vector3();
    8. }

             JointPoint类中其他的数据需要在肢体驱动时自己推算:

    1. public class JointPoint
    2. {
    3. public Transform Transform = null;
    4. public Quaternion InitRotation;
    5. public Quaternion Inverse;
    6. public Quaternion InverseRotation;
    7. public JointPoint Child = null;
    8. public JointPoint Parent = null;
    9. }

    (2)算法理论解析

            解决抖动:卡尔曼滤波+低通滤波: 关键点平滑方案

            算法模型解析:模型解析heatmap&offset

    • 有三个input,其实都是一样,都要输入(448,448,3)的图片:
    1. private void UpdateVNectAsync()
    2. {
    3. input = new Tensor(videoCapture.MainTexture, 3);
    4. if (inputs[inputName_1] == null)
    5. {
    6. inputs[inputName_1] = input;
    7. inputs[inputName_2] = new Tensor(videoCapture.MainTexture, 3);
    8. inputs[inputName_3] = new Tensor(videoCapture.MainTexture, 3);
    9. }
    10. else
    11. {
    12. inputs[inputName_3].Dispose();
    13. inputs[inputName_3] = inputs[inputName_2];
    14. inputs[inputName_2] = inputs[inputName_1];
    15. inputs[inputName_1] = input;
    16. }
    17. if (!Lock && videoCapture.IsPlay())
    18. {
    19. StartCoroutine(ExecuteModelAsync());
    20. }
    21. }
    • 四个output,但我们只用后两个:
    1. for (var i = 2; i < _model.outputs.Count; i++)
    2. {
    3. b_outputs[i] = _worker.PeekOutput(_model.outputs[i]);
    4. }
    5. offset3D = b_outputs[2].data.Download(b_outputs[2].shape);
    6. heatMap3D = b_outputs[3].data.Download(b_outputs[3].shape);

            用heatmap粗略定位关节位置,然后使用offset在heatmap结果上精确调整关节位置。        

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

    • 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坐标偏移。

    2.Unity肢体驱动

            项目中肢体驱动的代码主要为VNectModel.cs。

    (1)相关知识

    • 四元素的逆,⽐如⼀个四元数为(1,1,1,1)它的逆是(-1,-1,-1,1)。
    Quaternion Inverse(Quaternion rotation);

            返回⼀个相反的旋转。

    • 注视旋转
    public static Quaternion LookRotation(Vector3 forward, [DefaultValue("Vector3.up")] Vector3 upwards);

            创建一个指定的forward 和upward方向的旋转,返回一个计算的四元数。如果用来确定一个transform:如果向量是正交的, z轴将和forward对齐,y轴将和upward对齐。如果forward方向是0将会报错。

    (2)项目中的使用

            由当前关节的lookrotation=初始旋转InitRotation×对齐矩阵 及AA^{-1}=1可得出:

    a. Quaternion.Inverse(对齐矩阵)=当前旋转Rotation*Quaternion.Inverse(当前关节的lookrotation)

    1. hip.Inverse = Quaternion.Inverse(Quaternion.LookRotation(forward));
    2. hip.InverseRotation = hip.Inverse * hip.InitRotation;

            函数Init()中有大量类似的代码,目的是求中间矩阵,根据中间矩阵,我们可以通过算法数据得出当前object的旋转。VNectModel.cs中的hip.InverseRotation就是Quaternion.Inverse(对齐矩阵)

    b. 当前旋转Rotation=当前关节的lookrotation×Quaternion.Inverse(对齐矩阵)

    1. //基于根关节和左右胯关节坐标计算出人体朝向,然后以此作为所有关节LookRotation的y方向
    2. var forward = TriangleNormal(jointPoints[PositionIndex.hip.Int()].Pos3D, jointPoints[PositionIndex.lThighBend.Int()].Pos3D, jointPoints[PositionIndex.rThighBend.Int()].Pos3D);
    3. jointPoints[***].Transform.rotation = Quaternion.LookRotation(Vector3.up, forward) * jointPoint[***].InverseRotation;

            函数PoseUpdate()用于肢体驱动,上面这行代码就是用来计算关节的当前旋转的。

    c. 对齐矩阵=当前关节的lookrotation*Quaternion.Inverse(当前旋转Rotation);

    1. root = animator.GetBoneTransform(HumanBodyBones.Hips);
    2. midRoot = Quaternion.Inverse(root.rotation) * Quaternion.LookRotation(forward);//midRoot 为对齐矩阵

             这些代码在项目中不存在,只是为了更好的解释公式。

  • 相关阅读:
    RabbitMQ学习笔记(一)(概述)
    同城跑腿微信小程序源码系统完整搭建教程
    sklearn实现决策树,随机森林,逻辑回归,KNN,贝叶斯,SVM,以葡萄干数据集为例
    Continual Learning of Large Language Models: A Comprehensive Survey
    变电站自动化监控系统
    .Net学习——Nlog日志框架的使用
    关于有道云笔记中脑图展示问题的研究
    Prompt Engineering,提示工程
    A-Level经济真题
    【第28例】IPD体系进阶 | 需求管理:需求实现过程
  • 原文地址:https://blog.csdn.net/weixin_39766005/article/details/125547151