1. 程式人生 > 實用技巧 >【論文筆記+復現踩坑】End-to-end Recovery of Human Shape and Pose(CVPR 2018)

【論文筆記+復現踩坑】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