最近笔者遇到一个问题就是openpose在对于姿态识别的时候,识别出的人体姿态是一个2d平面姿态。对于AR交互应用来说,2d姿态是不如3d姿态的。当然可以使用kinect深度摄像头或者开源项目vnect实现3d的实时姿态识别。但是如果能够对于openpose做一些改动,可以达到近似的效果。
首先openpose的body_25模型输出如图:
为了做出一个近似效果 ,我们可以先假定
1.躯干是面对摄像头不会偏移(旋转的)【关节点 5 2 1 8】。
2. 肢体的倾斜只有左倾 右倾 和 前倾 , 没有向后倾。
然后首先我们的目标是做一个如图上的伸展姿势(肢体在2d平面内),测定每块肢体的长度。接下来 ,我们以 1 2 和 2 3 关节点为例,做一个3d姿态识别。
首先我们假定了 5 2 1 8 始终与摄像头保持平行,所以可以以此为参照。
对于2d平面来说 这个角度我们是已知的,可以通过计算向量之间的夹角得出。
那么当3进行前倾的时候, 得到的2d 输出其实是 2 3'。我们已知 2 3'的长度和 2 3的长度。
2 3' = 2 3 * cos Θ。 这样可以计算得出 前倾角θ的角度。得到了 两张图中的角度 ,即可得到关节点3的空间位置
对于OpenPose的unity插件而言,是将得到的姿态信息在屏幕上进行标定,然后使用LineRenderer进行绘制。笔者在此基础上添加了肢体模型
脚本在lsfather lsfather为空物体 lshoulder为实际模型,这样方便于角度和模型的调校。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class To3DBlog : MonoBehaviour
{
[SerializeField] RectTransform Joint0;
[SerializeField] RectTransform Joint1;
Vector3 direction;
bool enabledstate;
GameObject Renderobj;
[SerializeField] float MaxSize = 0;
[SerializeField] float curLength;
float curHeight;
[SerializeField] GameObject childobj;
// Start is called before the first frame update
void Start()
{
Renderobj = this.gameObject;
childobj = this.gameObject.transform.GetChild(0).gameObject;
}
// Update is called once per frame
void Update()
{
if (Joint0 && Joint1)
{
bool enabled = Joint0.gameObject.activeInHierarchy && Joint1.gameObject.activeInHierarchy;
enabledstate = enabled;
childobj.SetActive(enabled);
if (enabled)
{
//进行
direction = (Joint0.transform.localPosition - Joint1.transform.localPosition);
curLength = direction.magnitude;
//向量的模
if (curLength > MaxSize)
{
MaxSize = direction.magnitude;
}
//求Joint1 的 z 值
//curHeight = MaxSize * Mathf.Sin(Mathf.Acos(curLength/MaxSize));
curHeight = Mathf.Sqrt(Mathf.Pow(MaxSize,2)- Mathf.Pow(curLength,2));
//面前方向是 -z 轴方向 但是 考虑镜像
direction = (Joint1.transform.localPosition + new Vector3(0, 0, curHeight) - Joint0.transform.localPosition);
Renderobj.transform.localPosition = (Joint1.transform.localPosition + Joint0.transform.localPosition + new Vector3(0,0,-curHeight)) / 2;
Renderobj.transform.up = direction;
Renderobj.transform.localScale = new Vector3(MaxSize, MaxSize, MaxSize);
//scale的问题
}
}
}
}
算法解释如图:其中curLength 是openpose识别出两个关节点间的距离(也是该肢体在z轴的投影),MaxSize是该肢体实际长度,以此可以求出当前Joint1的高度。
对于右肩适用结果
对于右手臂适用结果 左2d 右3d 效果如图