作者 | 李秋鍵
出品 | AI科技大本營(ID:rgznai100)
引言
隨著人機互動技術飛速發展,人體姿態估計技術越來越受到重視。姿態估計作為人體行為辨識的重要組成部份,近年來逐漸成為電腦視覺領域的一個重要的研究熱點。由於人體結構和姿態的復雜性以及視覺理論的局限性,最初人體姿態估計演算法僅從影像或者影片當中預測人體二維骨架節點的座標位置。2015年馬普所提出了由姿態與體型參數驅動的蒙皮多人線性模型,由於該模型具有出色的建模效果與快速的計算效率,許多團隊提出了利用該模型進行人體姿態估計的方法。目前基於人體形變模型的姿態估計方法可以根據兩個標準進行分類:一類是基於最佳化的方法,另一類是基於回歸的方法。而最終發展到現在三維人體姿態估計也隨之發展越來越成熟。
今天我們就將使用Python+Unity3d實作一個基於ThreeDPoseUnityBarracuda(Digital- Standard Co., Ltd.)的3D虛擬現實互動遊戲。這裏透過Unity3d結合python三維姿態估計模型,即時獲取人體三維座標,然後將座標與人體模型骨骼繫結從而達到控制3D角色的目的。在這步基礎上加入3D模型,設定基本的觸碰邏輯即可達成我們設定的簡單遊戲的目的。這裏使用到的模型可以透過3Dmax和Blender進行繪制,最終的演示效果如下:
![](https://img.jasve.com/2024-2/484c901807ea51e6aef36164129aa323.webp)
三維姿態控制介紹
在三維姿態估計的基礎上,使用unity對即時獲取的三維骨骼座標和角色骨骼繫結,控制角色動畫,達到互動的效果。
1.1 三維姿態估計介紹
人體姿態估計的主要任務是預測出人體關節點的三維座標位置和角度等資訊。由於人體姿態標記數據集的缺乏,使得大多數研究方法都基於2D人體姿態估計方法之上,因此2D人體姿態估計研究的發展也為3D人體姿態估計奠定了基礎,使得3D人體姿態估計研究有著巨大的潛力。
在實際套用中,由於3D姿態估計在2D姿態估計的基礎上加入了深度資訊,其對於人體姿態的表述比2D更為精準,因此其套用範圍和研究價值都要高於2D人體姿態估計,但是3D姿態估計的難度也更高,存在著遮擋,單視角2D到3D的對映中固有的深度模糊性、不適定性,缺少大型的室外數據集等挑戰。
在目前的研究中,三維人體姿態估計方法可以劃分為傳統方法和深度學習方法兩類。在深度學習方法得到廣泛套用之前,3D人體姿態標註數據集和具有高運算能力的GPU還沒有普及,研究人員主要透過一些套用在傳統電腦視覺或機器學習領域的方法來進行3D人體姿態的估計。傳統三維人體姿態估計和基於深度學習的姿態估計之間最明顯的特征在於是否使用了多層神經網絡的學習方法,因為建模方式不同,在估計精確性、計算復雜度等方面也有著較大的差別。其中建模是三維人體姿態估計一個很重要的方面,目的是表示從輸入數據中提取的關鍵點和特征。在解決實際問題時由於實驗個體所處環境的復雜性,很大程度上增加了模型的建立難度,因此選取適當且有效的影像特征來簡化模型建立過程十分重要。傳統方法很多是采用基於人體模型的方法來描述和推斷人體姿態,透過演算法提取影像姿態特征,因此對特征表示和關鍵點的空間位置關系這兩個維度有比較高的要求,除去邊界、顏色這類低層次特征,典型的有尺度不變特征變換、梯度直方圖等表達能力更強、可有效壓縮特征空間維度的高層次特征,它們雖然在時間效率方面具有優勢,但依然是由人工設計的傳統特征,存在著較大的不足。
![](https://img.jasve.com/2024-2/2ce5b70726b1d5d53dafd41cc870ad66.webp)
1.2 ThreeDPoseUnityBarracuda介紹
ThreeDPoseUnityBarracuda透過讀取Barracuda的onnx三維姿態估計模型,可以在Unity上做三維姿態估計。
程式設計
這裏三維姿態估計使用Resnet34_3inputs_448x448_20200609.onnx模型,unity3D是對onnx模型的呼叫,同時搭建三維場景和設計邏輯規則。
2.1 三維姿態估計模型解析
這裏需要使用的三維姿態估計在之前文章「3DPose實作三維人體姿態辨識」有所介紹,不進行太多描述。
透過使用onnxruntime讀取「Resnet34_3inputs_448x448_20200609.onnx」模型檔,即時對需要辨識的圖片數據,獲取每一張圖片的offset圖和heatmap圖。透過找到第j個關節的28個特征圖,並找到最大值的索引來獲取個點座標。並把座標按照一定比例縮放。使得影像變形較為符合人體規律。
for j in range(0, 24):
# 找到第j個關節的28個特征圖,並找到最大值的索引
joint_heat = heatMap3D[j * 28:(j + 1) * 28, ...]
if np.max(joint_heat)>0.1:
print(np.max(joint_heat))
[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圖,並計算3D座標的xyz值
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
kps[j, 0] = pos_x
kps[j, 1] = pos_y
kps[j, 2] = pos_z
else:
try:
kps[j, 0] = kps[j-1, 0]
kps[j, 0] = kps[j-1, 0]
kps[j, 2] = kps[j-1, 2]
except:
pass
parent = np.array([15, 1, 2, 3, 3, 15, 6, 7, 8, 8, 12, 15, 14, 15, 24, 24, 16, 17, 18, 24, 20, 21, 22, 0]) - 1;
for i in range(len(kps)):
if (parent[i] != -1):
ax.plot3D(kps[[i, parent[i]], 0], -kps[[i, parent[i]], 1], -kps[[i, parent[i]], 2], 'gray')
![](https://img.jasve.com/2024-2/355f0a1a6ee37a1d7f766e7979be1ef0.webp)
2.2 unity3D程式設計
Unity3D這裏主要使用到了三維場景搭建和CS指令碼制定邏輯,這裏場景搭建,在搭建好模型後手動規劃即可。主要介紹程式部份。
1、讀取模型,按照下圖配置即可:
![](https://img.jasve.com/2024-2/3c48a7f3b3480e4b07b0f5407b9848bb.webp)
2、隨機從天空掉落物體CS指令碼:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Randomoccurs : MonoBehaviour
{
//隨機產生的物體
private static GameObject sphere;
private static GameObject cube;
private static GameObject cylinder;
private static GameObject capsule;
public GameObject[] gameobject =
{
sphere,
cube,
cylinder,
capsule
};
//想要產生幾波
public int waves;
//每波產生的數量
public int values;
//產生之後延遲時間
public float spawnwait ;
// Use this for initialization
void Start()
{
StartCoroutine(test01());
}
void Update()
{
}
// Update is called once per frame
IEnumerator test01()
{
for (int j = 0; j < waves; j++)
{
for (int i = 0; i < values; i++)
{
Instantiate(gameobject[Random.Range(0, 4)], transform.position, transform.rotation);
}
yield return new WaitForSeconds(spawnwait);
}
}
}
3、制定觸碰規則,碰到門,門對應ID設定為銷毀,碰到掉落物體,分數加分:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class hit_obj : MonoBehaviour
{
// Start is called before the first frame update
static public int score = 0;
void Start()
{
}
// Update is called once per frame
void Update()
{
}
// 碰撞開始
void OnTriggerEnter(Collider collider)
{
var tag = collider.tag;
if (collider.tag == "body_center")
{
score += 1;
GameObject.Destroy(gameObject);
//GameObject.Destroy(gameObject, 2.0f);//摧毀自身
}
Debug.Log(score);
GameObject.Find("Canvas/Score").GetComponent<Text>().text = "得分:"+score.ToString();
if (collider.tag == "ground")
{
//Debug.Log("銷毀" + gameObject.tag);
GameObject.Destroy(gameObject);
}
}
// 碰撞結束
void OnTriggerStay(Collider collider)
{
}
// 碰撞持續中
void OnTriggerExit(Collider collider)
{
}
}
![](https://img.jasve.com/2024-2/93754ca7233c611dabcb7c822201ea04.webp)
完整程式碼:
連結:
https:// pan.baidu.com/s/1hZ5f-4 Vv12rpJXK5XL_t5A
提取碼:7q6o
李秋鍵,CSDN網誌專家,CSDN達人課作者。碩士在讀於中國礦業大學,開發有taptap競賽獲獎等。