PS. 這里做的論文筆記主要是為自己方便回顧。
概述
做了什么:引入一個端到端的Human Mesh Recovery框架,從包含人體的RGB位圖中重建出一個SMPL的3D網格,並嘗試重新投影回圖片上
目的:最小化關鍵點的重投影損失,使得我們可以使用只帶2D准確標注的戶外場景圖像就能進行訓練
難點:
- 缺乏自然場景下的大規模ground truth的3D數據集
- 單視角下2D到3D映射所固有的模糊性(缺乏深度信息),可能會導致產生的模型異常,如自相交、異常的人體姿勢等情況
- 預測相機視角又會在人體大小和攝像機的距離建引入額外的scale ambiguity
- 旋轉的回歸問題
關鍵點:
- 在從圖片推斷出3D人體模型后,將它重投影到2D圖片上計算2D損失
- 引入生成對抗網絡(GANs),通過訓練鑒別器來推斷生成的3D人體模型是否為真人,且形體姿勢是否合理(需要3D訓練集)(鑒別器能夠學到3D關節角度的限制)
- 由於歐拉角旋轉表示法存在多對一的映射,轉換成旋轉矩陣可以保證其唯一性
優點:
- 直接從圖像推斷出SMPL參數
- 可以直接生成網格
- 方法是端到端的
- 可以不需要使用配對的2D-3D數據集,並且不依賴中間2D關鍵點偵測
- 運行效率可以達到實時級別(1080Ti在部件分割任務上用時0.04sec)
現有的一些從2D圖片恢復人體3D網格的方法專注於恢復人體3D關節點的坐標位置。但問題在於:
- 關節點是離散的,但人體在3D空間的表示是密集連續的
- 3D關節點位置本身並不能約束每個關鍵點之間的關系,僅通過這些位置並不能很好的預測人體姿勢和體型
論文的做法:
- 為kinematic tree中的每個3D關鍵點輸出相對的3D旋轉矩陣,來捕獲3D的頭部和肢體角度方向。預測角度還可以確定肢體的對稱性和肢體長度等信息的合理性
- 該模型從3D人體模型數據集中能夠學習到3D關節角度的限制
論文具體做法
數據集輸入:帶2D關節點Ground Truth的圖像數據
Encoder
使用ResNet-50網絡對圖像進行編碼
- Input:224x224 RGB圖像
- Output:經過平均池化后的特征\(\phi\in\mathbf{R}^{2048}\)
3D Regression
-
Input:concat后的\([\phi, \Theta_t]\),初始的\(\Theta_0\)源自
neutral_smpl_mean_param.h5
-
Layer 1:
Linear(2048 + 85, 1024), ReLU(), Dropout(0.5)
-
Layer 2:
Linear(1024, 1024), ReLU(), Dropout(0.5)
-
Layer 3:
Linear(1024, 85)
-
Output:\(\Delta\Theta_t\)
Iterative error feedback(IEF):計算出殘差\(\Delta\Theta_t\)后進行加法更新:\(\Theta_{t+1}=\Theta_{t} + \Delta\Theta_t\)
其中\(\Theta=\{\mathbf{\theta}, \mathbf{\beta}, R, t, s\}, \mathbf{\theta}\in\mathbf{R}^{3K}, \mathbf{\beta}\in\mathbf{R}^{10},R\in\mathbf{R}^{3},t\in\mathbf{R}^{2},s\in\mathbf{R}\).K=23
,θ為SMPL關節點的軸角,β控制SMPL體型,R為全局軸角,t為攝像機xy平面的平移量,s為攝像機的縮放量
\(M(\mathbf{\theta}, \mathbf{\beta})\):代表SMPL模型的N=6980
3D頂點
\(X(\mathbf{\theta}, \mathbf{\beta})\):代表SMPL模型的23個3D關節點
對3D關節點的投影:\(\hat{\mathbf{x}}=s\Pi(RX(\mathbf{\theta}, \mathbf{\beta})) + t\),\(\Pi\)為正交投影
損失函數:
如果有3D ground truth,則對應的annotation為\([\mathbf{\beta}, \mathbf{\theta}]\)。網絡輸出則為\([\hat{\mathbf{\beta}}, \hat{\mathbf{\theta}}]\)
- 2D Loss:\(L_{reproj}=\sum_i\parallel v_i(\mathbf{x}_i - \hat{\mathbf{x}}_i)\parallel_{1}\),\(v_i\)為2D關節點i的可視性(1可見,0不可見)
- 3D SMPL Loss:\(L_{smpl}=\parallel[\mathbf{\beta_i}, \mathbf{\theta_i}] - [\hat{\mathbf{\beta_i}}, \hat{\mathbf{\theta_i}}]\parallel_2^2\)
- 3D Joint Loss:\(L_{joints}=\parallel(\mathbf{X}_i - \hat{\mathbf{X}}_i)\parallel_2^2\)
- 3D Loss:\(L_{3D}=L_{smpl}+L_{joints}\)
Discriminator
- Input:\(\beta, \theta\)
Shape Discriminator:
- Layer 1:
Linear(10, 5), ReLU()
- Layer 2:
Linear(5, 1)
Pose Discriminator(C=9是因為軸角變成旋轉矩陣):
-
Input:
NHWC = [N, 23, 1, 9]
-
Layer 1:
Conv2d(out_c=32, k=1x1), ReLU()
-
Layer 2:
Conv2d(out_c=32, k=1x1), ReLU()
For pose respectively(K):
- Layer 3:
[N, 1, 1, 32]---Fully Connected--->Linear(32, 1)--->[N, 1]
For all pose(1):
- Layer 3:
[N, 23, 1, 32]=FC1024, ReLU() =>[N, 1024]
- Layer 4:
FC1024, ReLU()
- Layer 5:
FC1
- Layer 3:
Total:K+2 Discriminator
損失函數:
- Adversarial Loss for the encoder:\(min L_{adv}(E)=\sum_i\mathbf{E_{\Theta\sim p_E}[(D_i(E(I))-1)^2]}\)
- Objective for each discriminator:\(min L(D_i)=\sum_{i}\mathbf{E_{\Theta\sim p_{data}}}[(D_i(\Theta)-1)^2] + \mathbf{E_{\Theta\sim p_E}}[D_i(E(I))^2]\)
- Objective for encoder:\(L=\lambda(L_{reproj}+\mathbf{1}\ L_{3D})+L_{adv}\),這里1代表是否有ground truth 3D數據
實驗
評價指標:
- Reconstruction: mean per joint position error(MPJPE) 、 Reconstruction error、PCK、AUC
- Part segmentation: Acc、F1-score
測試數據集:Human3.6M,MPI-INF-3DHP
實驗方法:
- T1、T2:對Human3.6M用不同方法評估Reconst. Error
- T3:對MPI-INF-3DHP,控制剛體對齊
- T4:部件分割
- Fig:對比使用/不使用配對的2D-to-3D監督
復現Demo踩坑
踩這個項目的坑踩了我好久,這里把環境配置的過程簡單整理下。
復現主要環境:
- Linux Ubuntu 18.04
- Anaconda3
- Python2.7
先按順序安裝下面這些包:
包 | 版本 | 安裝源 |
---|---|---|
cudatoolkit | 9.0 | conda |
cudnn | 7.6.5 | conda |
numpy | 1.14.0 | pip |
tensorflow-gpu | 1.12.0 | pip |
numpy的版本不要太新,不然后續編譯使用opencv2可能會帶來一系列麻煩。
tensorflow-gpu使用的是項目推薦的版本
然后用下面的代碼測試即可,得到True為成功:
import tensorflow as tf
print(tf.test.is_gpu_available())
編譯安裝opencv2
為了使用cmake編譯opencv2,這里需要先安裝一些東西:
$ sudo apt-get install build-essential
$ sudo apt-get install cmake
$ sudo apt-get install pkg-config
因為我們用的是python2.7,pip提供的opencv-python主要都是給python3.x用的,為此我們需要自己編譯一個。
這里我選擇的是opencv-2.4.13.6的版本:https://gitee.com/dhfhub/opencv/tree/2.4.13.6/
下載zip后解壓,終端跳到目錄opencv-2.4.13.6內,新建文件夾並進入,運行cmake。注意一定要是在hmr的虛擬環境下進行:
$ mkdir build
$ cd build
$ cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ../opencv
生成完畢后,開始安裝:
$ sudo make install
安裝完畢后運行python測試opencv2,此時應該沒有問題:
$ python
>>> import cv2
安裝剩余包
為了編譯安裝opendr,需要先運行下面命令安裝:
$ sudo apt install libosmesa6-dev
$ sudo apt-get install build-essential
$ sudo apt-get install libgl1-mesa-dev
$ sudo apt-get install libglu1-mesa-dev
$ sudo apt-get install freeglut3-dev
最后根據hmr項目里的requirements.txt來完成剩余安裝:
包 | 版本 | 安裝源 |
---|---|---|
scipy | 1.2.3(默認最新) | pip |
opendr | 0.78(默認最新,不能是0.77) | pip |
matplotlib | 2.2.5(默認最新) | pip |
scikit-image | 0.14.5(默認最新) | pip |
deepdish | 0.3.6(默認最新) | pip |
absl-py | 0.10.0(默認最新) | pip |
ipdb | 0.13.4(默認最新) | pip |
tensorflow-estimator | 1.10.12(降級避免出現ts.estimator找不到問題) | pip |
嘗試運行
回到hmr項目的目錄,執行:
$ wget https://people.eecs.berkeley.edu/~kanazawa/cachedir/hmr/models.tar.gz && tar -xf models.tar.gz
獲取模型文件后解壓到hmr文件夾內,得到models的文件夾
然后嘗試執行:
$ python -m demo --img_path data/coco1.png
此時可能還有一個報錯:
TypeError: load() got an unexpected keyword argument 'encoding'
python-BaseException
Process finished with exit code 1
找到src/tf_smpl/batch_smpl.py
,將dd = pickle.load(f, encoding="latin-1")
里的encoding部分刪掉,然后再嘗試再次執行。這時候應該能跑出結果了。
執行:
$ python -m demo --img_path data/im1954.jpg