【論文筆記+復現踩坑】End-to-end Recovery of Human Shape and Pose(CVPR 2018)

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關鍵點偵測

現有的一些從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=69803D頂點

\(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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章