3D姿态估计——ThreeDPose项目简单易用的模型解析
前言
之前寫過tensorflow官方的posenet模型解析,用起來比較簡單,但是缺點是只有2D關鍵點,本著易用性的原則,當然要再來個簡單易用的3D姿態估計。偶然看見了ThreeDPose的項目,感覺很強大的,所以把模型扒下來記錄一下調用方法。
參考博客:
ThreeDPose官方代碼
微軟的ONNX模型解析庫
ONNX解析庫的pythonAPI文檔
3D姿態估計最大的好處就是卡通角色的肢體驅動了,其實就是單目攝像頭的動捕方法。
理論和代碼解析
可以從官方去下載模型,戳這里,或者在文末的百度網盤下載。
模型結構
模型已經被作者轉換成ONNX的模型文件了,所以下載netron軟件去可視化模型,打開以后可以發現模型的網絡結構和輸入輸出
這里說明一下各部分含義:
有三個input,其實都是一樣,都要輸入(448,448,3)的圖片
有四個output,解析模型即提取關鍵點的時候,只需要第3和4個輸出,具體解析方法看下面代碼解析。
代碼解析
在windows上為了讀取ONNX的模型文件,可以使用微軟提供的onnxruntime這個庫,直接pip安裝即可,這個庫的文檔見上面參考博客的2和3。
先引入相關的庫文件
import numpy as np import onnxruntime as rt import cv2import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D首先就是加載模型,輸入圖片,推斷當前圖片的四個輸出
#加載模型 sess = rt.InferenceSession("Resnet34_3inputs_448x448_20200609.onnx") inputs = sess.get_inputs() #讀取圖片 img = cv2.imread("D:/photo/pose/5.jpg") img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) img = cv2.resize(img,(448,448)) img = img.astype(np.float32)/255.0 img = img.transpose(2,1,0) img = img[np.newaxis,...] img.shape #輸入到網絡結構中 pred_onx = sess.run(None,{inputs[0].name:img,inputs[1].name:img,inputs[2].name:img })獲取輸出
offset3D = np.squeeze(pred_onx[2]) heatMap3D = np.squeeze(pred_onx[3]) print(offset3D.shape)#(2016, 28, 28) print(heatMap3D.shape)#(672, 28, 28) print(offset3D.shape[0]/heatMap3D.shape[0])#3.0按照參考博客1的unity代碼解析輸出,可以發現這個命名和posenet的解析一模一樣,使用heatmap粗略定位關節位置,然后使用offset在heatmap結果上精確調整關節位置。
heatmap的 (672,28,28)(672,28,28)(672,28,28) 代表 24個關節的28個大小為(28,28)(28,28)(28,28)的特征圖。而offset比heatmap的特征圖多三倍,很明顯就是剛才說的精確定位,只不過需要在offset中定位到x,y,z三個坐標,所以就是三倍關系了。
定位原理就是:heatmap相當于把原圖劃分為(28,28)(28,28)(28,28)的網格點,每個網格點代表附近有一個關節的概率,通過找到某個關節最可能在heatmap哪一副特征圖的哪個網格,我們根據heatmap的索引在offset中找到對應的3個精確矯正xyz坐標的特征圖,這三個特征圖的值就是對應關節的xyz坐標相對于當前heatmap網格位置的精確偏移量。
在寫代碼之前,著重關注一下heatmap和offset的特征圖相對于關節是一個怎樣的順序。
- 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坐標偏移。
說了一堆,不如看代碼簡單明了:
比如提取第j個關節的3D坐標位置,對應的特征圖是[j?28,(j+1)?28?1][j*28, (j+1)*28-1][j?28,(j+1)?28?1] ,然后從這里面找到最大值的位置就是當前關節最可能在哪個特征圖的哪個網格位置上。
# 找到第j個關節的28個特征圖,并找到最大值的索引 joint_heat = heatMap3D[j*28:(j+1)*28,...] [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,找到精確的矯正值
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每個坐標都是在當前網格的位置上加上精確的偏移量,因為xyz分別在offset上分別隔了24?2824*2824?28個特征圖,所以出現了y和z的第一個索引有變化。
如果還有疑問,建議去看看前面的2D姿態估計的解析,然后自己手寫一遍解析算法就理解了。
把所有關節的位置存到kps然后可視化看看
%matplotlib inlinefig = plt.figure() ax = fig.gca(projection='3d') ax.scatter3D(kps[:,0],-kps[:,1],-kps[:,2],'red') parent = np.array([0,1,2,3,3, 1,6,7,8,8, 12,15,14,15,24, 24,16,17,18, 24,20,21,22, 0])-1; for i in range(24):if(parent[i]!=-1):ax.plot3D(kps[[i,parent[i]],0], -kps[[i,parent[i]],1], -kps[[i,parent[i]],2], 'gray')ax.xaxis.set_tick_params(labelsize=10) ax.yaxis.set_tick_params(labelsize=10) ax.zaxis.set_tick_params(labelsize=10)ax.view_init(elev=10., azim=180)其實把關鍵點保存下來用matlab畫更好看
%test clear;clc;close all % a=dlmread("D:\code\python\ThreeDPose\unity_data\kps100.txt"); a=[0.292807,14.994286,6.560671; -0.123055,15.480020,8.367174; -2.666008,19.526012,9.689341; -3.994198,19.631011,9.902868; -3.537779,20.058028,9.978683; 1.467720,11.799177,6.442903; -1.372830,10.170244,5.793396; -2.116019,9.653587,3.315798; -1.332626,9.906492,1.420080; -2.152191,9.461523,2.985400; 0.378545,12.237494,4.861950; -0.739344,12.452946,4.971550; -0.678598,14.203241,5.388392; -0.671332,13.130285,4.690326; -1.256000,12.918788,5.623796; 0.216525,13.818989,12.736559; 0.793380,13.383041,17.217848; -0.301103,13.578390,22.771406; -0.406112,13.623824,23.280164; 0.380849,11.442492,12.643937; -2.110893,11.706800,18.199155; -1.205901,12.321800,22.871755; -2.116101,12.384523,24.476315; -0.500211,12.706436,11.490892];parent =[15,1,2,3,3, 15,6,7,8,8, 12,15,14,15,24, 24,16,17,18, 24,20,21,22, 0]; plot3(a(:,1),-a(:,2),-a(:,3),'ro','MarkerSize',2,'MarkerFaceColor','r') for i=1:24if(parent(i)~=0)line([a(i,1) a(parent(i),1)],[-a(i,2) -a(parent(i),2)],[-a(i,3) -a(parent(i),3)])end end axis equal axis off24個關節的名稱分別標注一下說一下吧,從unity代碼中的VNectModel.cs能找到,玩過骨骼動畫的基本知道每個單詞的代表的關節,這里就不畫骨骼結構圖了,自己對應到上面的關鍵點圖中即可。
rShldrBend, rForearmBend, rHand, rThumb2, rMid1, lShldrBend, lForearmBend, lHand, lThumb2, lMid1, lEar, lEye, rEar, rEye, Nose, rThighBend, rShin, rFoot, rToe, lThighBend, lShin, lFoot, lToe, abdomenUpper,后記
其實如果要做肢體驅動,還需要
- 根據上述關節計算額外的一些關節坐標,這一塊不在本博文的學習范圍內,博文只關注怎么簡單的使用這個模型去解析關節位置。
- 將關節坐標映射到原圖,這個根據比例原圖比例縮放一下即可,與posenet一樣,也不做闡述。
模型文件網盤地址:
鏈接:https://pan.baidu.com/s/1TsvALWJRIoCAtQ9ffcno7w
提取碼:rfow
本博文同步更新到微信公眾號中,有興趣可關注一波,代碼在微信公眾號簡介的github找得到,有問題直接公眾號私信。
總結
以上是生活随笔為你收集整理的3D姿态估计——ThreeDPose项目简单易用的模型解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue组件化之插槽
- 下一篇: Web APIs简介