一枚の画像に映る人物に対するSMPL上での部分的なtexture mapを取り出す

denseposeとsmplの組み合わせはよく見かけるので勉強がてら一つやってみる。

実装も上がっており、やっていることも最小限に見えるのでこちらの論文の前処理を題材とする。

やりたいことは、一枚の画像に映る人物をSMPLのtexture mapとして取り出すこと。

ちなみにSMPLについては以前記事を書いた
https://jsapachehtml.hatenablog.com/entry/2023/10/29/200455

人物領域の推定にはdenseposeを利用する

  • http://densepose.org/
  • image上の人物領域における各pixelについて以下を推定するモデル
    • どのパーツに属するか
    • そのパーツ上でどの位置にあるか
  • 様々な姿勢の人物画像とそのSMPL mesh上での対応点のペアを学習データとしている

パーツ分けについては位置の回帰がしやすいよう独自の分け方をしており、SMPLの元のtexture mapとは異なるが、同じmeshを参照してるものなので相互に変換できる。

denseposeでのIUV map作成

こちらの画像をサンプルとして使う
https://github.com/thmoa/tex2shape/blob/303b94f/data/images/spotlight_001.jpg

denseposeは論文が出た時点と実装の管理方法が変わっていて、現在はdetectron2の一部として統合されていた。

新しい方のrepoの実装を利用する。

論文にあるこちらの画像右のtexture map形式の画像を得ることが今回の目的であり、そのためにまず真ん中のようなIUV mapを得たい 。

denseposeの推論にはこちらのスクリプトが用意されており、入力画像に対する推論結果が画像化できる
https://github.com/facebookresearch/detectron2/blob/main/projects/DensePose/doc/TOOL_APPLY_NET.md#visualization-mode

ただ、入力画像に推論結果のmaskを重ねて表示するもののようで、IUV map自体の画像を出力してくれるオプションはなかった。中を見てみると例えばこのようにIUVを利用してる部分がある。
https://github.com/facebookresearch/detectron2/blob/4e80df1/projects/DensePose/densepose/vis/densepose_results_textures.py#L51

なのでこれだけ出力するよう簡易なオプションを実装して使った。
https://github.com/y-kamiya/detectron2/commit/c44db8b3e146fa6ac89825645492f2278a1b8425

apply_net.pyのドキュメントを参考に以下のコマンドでIUV mapを出力

python apply_net.py show configs/densepose_rcnn_R_50_FPN_DL_s1x.yaml R_50_FPN_DL_s1x.pkl spotlight_001.jpg dp_iuv_map --output spotlight_001.png  -v

これで左ような画像(=spotlight_001.0001.png)が取得できる(右は入力画像)

ちなみに環境構築に関してはこちらに書いてある通りで、python3.10系、torch==2.1.0でも問題なく動いた
https://github.com/facebookresearch/detectron2/blob/4e80df1/projects/DensePose/doc/GETTING_STARTED.md#installation-as-a-package

学習済みモデルについてはこちらから精度の高いものを選んで落とした
https://github.com/facebookresearch/detectron2/blob/4e80df1/projects/DensePose/doc/DENSEPOSE_IUV.md#-model-zoo-and-baselines

texture mapの作成

densepose用のformatからSMPLのformatへの変換部分について、論文著者が効率のためlookup tableを事前に用意したと書いてあり今回はそれを利用する

For easier mapping, we precompute a look-up table to convert from 24 DensePose UV maps to the single joint SMPL UV parameterization.

変換を行っているのはこちらの処理
https://github.com/thmoa/tex2shape/blob/504c69ee4a08f6e6ed42460645acd301f7fc680d/lib/maps.py#L6
入力画像の各pixel値をIUV mapが示すtexture map上の位置へコピーしている

この関数を使うように以下の簡易なスクリプト(=unwrap.py)を実装

import cv2
import os
import argparse
import numpy as np

from lib.maps import map_densepose_to_tex


def main(img_file, iuv_file):
    img = cv2.imread(img_file) / 255.
    iuv_img = cv2.imread(iuv_file)
    unwrap = (map_densepose_to_tex(img, iuv_img, 512) * 255).astype(np.int32)
    out_file = os.path.basename(img_file)
    cv2.imwrite(f"out/unwrap_{out_file}", unwrap)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('image', type=str, help="Input image")
    parser.add_argument('iuv', type=str, help="Densepose IUV image")
    args = parser.parse_args()
    print(args)

    main(args.image, args.iuv)

実行

python unwrap.py spotlight_001.jpg spotlight_001.0001.png

出力

だいぶスカスカのtexture mapになるが、正面から見た一枚のみから抽出した部分的なものなのでこうなる。

ちなみに論文だと、これを入力としてpix2pixの形式でnormal mapとdisplacement mapを予測するよう学習し、入力画像の3d形状を推定している。