目录
Digital Standard Co., LTD. (github.com)
参考文章:
项目中算法模型预测数据的代码主要为VNectBarracudaRunner.cs。函数PredictPose()中更新的节点数据为JointPoint中的三个变量(也就是驱动时要用到的算法数据):
- public class JointPoint
- {
- public Vector3 Pos3D = new Vector3();
- public float score3D;
- public bool Visibled;
- }
JointPoint类中还有几个变量是预测过程中用于数据优化(防抖动等)的:
- public class JointPoint
- {
- public Vector3 Now3D = new Vector3();
- public Vector3[] PrevPos3D = new Vector3[6];
- public Vector3 P = new Vector3();
- public Vector3 X = new Vector3();
- public Vector3 K = new Vector3();
- }
JointPoint类中其他的数据需要在肢体驱动时自己推算:
- public class JointPoint
- {
- public Transform Transform = null;
- public Quaternion InitRotation;
- public Quaternion Inverse;
- public Quaternion InverseRotation;
-
- public JointPoint Child = null;
- public JointPoint Parent = null;
- }
解决抖动:卡尔曼滤波+低通滤波: 关键点平滑方案
算法模型解析:模型解析 、 heatmap&offset
- private void UpdateVNectAsync()
- {
- input = new Tensor(videoCapture.MainTexture, 3);
- if (inputs[inputName_1] == null)
- {
- inputs[inputName_1] = input;
- inputs[inputName_2] = new Tensor(videoCapture.MainTexture, 3);
- inputs[inputName_3] = new Tensor(videoCapture.MainTexture, 3);
- }
- else
- {
- inputs[inputName_3].Dispose();
- inputs[inputName_3] = inputs[inputName_2];
- inputs[inputName_2] = inputs[inputName_1];
- inputs[inputName_1] = input;
- }
-
- if (!Lock && videoCapture.IsPlay())
- {
- StartCoroutine(ExecuteModelAsync());
- }
- }
- for (var i = 2; i < _model.outputs.Count; i++)
- {
- b_outputs[i] = _worker.PeekOutput(_model.outputs[i]);
- }
- offset3D = b_outputs[2].data.Download(b_outputs[2].shape);
- 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三个坐标,所以就是三倍关系了。
项目中肢体驱动的代码主要为VNectModel.cs。
Quaternion Inverse(Quaternion rotation);
返回⼀个相反的旋转。
public static Quaternion LookRotation(Vector3 forward, [DefaultValue("Vector3.up")] Vector3 upwards);
创建一个指定的forward 和upward方向的旋转,返回一个计算的四元数。如果用来确定一个transform:如果向量是正交的, z轴将和forward对齐,y轴将和upward对齐。如果forward方向是0将会报错。
由当前关节的lookrotation=初始旋转InitRotation×对齐矩阵 及可得出:
a. Quaternion.Inverse(对齐矩阵)=当前旋转Rotation*Quaternion.Inverse(当前关节的lookrotation)
- hip.Inverse = Quaternion.Inverse(Quaternion.LookRotation(forward));
- hip.InverseRotation = hip.Inverse * hip.InitRotation;
函数Init()中有大量类似的代码,目的是求中间矩阵,根据中间矩阵,我们可以通过算法数据得出当前object的旋转。VNectModel.cs中的hip.InverseRotation就是Quaternion.Inverse(对齐矩阵)。
b. 当前旋转Rotation=当前关节的lookrotation×Quaternion.Inverse(对齐矩阵)
- //基于根关节和左右胯关节坐标计算出人体朝向,然后以此作为所有关节LookRotation的y方向
- var forward = TriangleNormal(jointPoints[PositionIndex.hip.Int()].Pos3D, jointPoints[PositionIndex.lThighBend.Int()].Pos3D, jointPoints[PositionIndex.rThighBend.Int()].Pos3D);
-
- jointPoints[***].Transform.rotation = Quaternion.LookRotation(Vector3.up, forward) * jointPoint[***].InverseRotation;
函数PoseUpdate()用于肢体驱动,上面这行代码就是用来计算关节的当前旋转的。
c. 对齐矩阵=当前关节的lookrotation*Quaternion.Inverse(当前旋转Rotation);
- root = animator.GetBoneTransform(HumanBodyBones.Hips);
- midRoot = Quaternion.Inverse(root.rotation) * Quaternion.LookRotation(forward);//midRoot 为对齐矩阵
这些代码在项目中不存在,只是为了更好的解释公式。