TADAをimage-3dへ拡張

TADAは元々text-3dの手法でimage条件付けの実装は含まれていない。 しかし、それだとstable diffusionが知ってるキャラクターしか作れないため、画像からavatar生成ができるよう実装を加えてみたのでメモ。

guidanceの部分でreference imageを元にした生成ができれば元と同じようにsdsを用いて3d生成が可能と考えられる。reference imageを元にした画像生成はいろいろ方法があるが、今回はimage条件付け可能なmultivew diffusionであるImageDreamを利用することにした。

ImageDreamについてはこちらに書いた
ImageDreamの調査 - MEMOcho-

実装調査

ImageDreamはthreestudio上に実装されており、guidance部分はここ

https://github.com/bytedance/ImageDream/blob/26c3972/threestudio/models/guidance/multiview_diffusion_guidance.py

ImageDreamもsdsによって3d生成する形なので、基本的にはこれをまるごと使うことが可能なはず。ただしmultiview diffusionであり、diffusionの推論を行う際にはImageDreamの生成条件に合わせたcamera poseの情報がcontextとして必要となる。

そして、camera poseを作っているのがこちら

https://github.com/bytedance/ImageDream/blob/26c3972e586f0c8d2f6c6b297aa9d792d06abebb/threestudio/data/random_multiview.py#L38

datasetとしてcamera poseを作り、その条件で3d表現からのレンダリングを実行、レンダリングした画像とそのcamera poseをguidanceのinputに与えてsdsという流れ。

TADA側も流れとしては同様になっている。ということで、datasetの部分もImageDreamのものに差し替えてしまうのが最も手間がかからなさそう。

実装

座標系合わせ

ImageDream側のdatasetとguidance用のclassを、TADAの学習時の処理から使うようにしてみたところ、レンダリング結果がこんな状態になる

これは座標系の問題

具体的にはelevation/azimuthの設定からcamera位置を決める部分の計算が異なる。それぞれの定義部分
https://github.com/TingtingLiao/TADA/blob/e8e39c048a69da9c63413643ff2af5caef845a4f/lib/provider.py#L214-L218 https://github.com/bytedance/ImageDream/blob/26c3972e586f0c8d2f6c6b297aa9d792d06abebb/threestudio/data/random_multiview.py#L118-L128

ちなみにopenglblender座標系の違いを表にまとめていてわかりやすかった

BlenderとUnityの座標系の違いを治す|KitanoMinami

角度のズレ

座標系は合わせることができたが、以下の2つの画像が水平方向に90度ズレる

レンダリングや生成条件として渡すcamera poseは当然座標系をあわせたものを使っている。

もう一度↑に貼ったcamera位置の定義を見てみる

TADA

centers = torch.stack([
    radius * torch.sin(thetas) * torch.sin(phis),
    radius * torch.cos(thetas),
    radius * torch.sin(thetas) * torch.cos(phis),
], dim=-1) + shift  # [B, 3]

ImageDream

camera_positions: Float[Tensor, "B 3"] = torch.stack(
    [
        camera_distances * torch.cos(elevation) * torch.cos(azimuth),
        camera_distances * torch.cos(elevation) * torch.sin(azimuth),
        camera_distances * torch.sin(elevation),
    ],
    dim=-1,
) + shift

水平方向の角度はそれぞれphis, azimuthであり、これを0にして考えてみるとTADAはz軸、ImageDreamはx軸が基準となっていることがわかる。

それぞれのコードベースはこれを前提に処理しているはずなので同じcamera poseを与えれば、TADAのレンダリングはz軸からの角度、ImageDreamであればx軸からの角度として扱うために90度ズレる。

ちなみにelevationの角度の基準も違っており、片方は水平面から、他方は頭頂方向から見た角度で設定されている点も異なる。ただ、こちらの場合は設定値もこれに合わせて対応した値が設定される前提になっていたため問題なかった。

座標系合わせと角度ズレの修正部分のみならこれだけ

https://github.com/y-kamiya/TADA/commit/a3bc9dcdc49e0d75b9066627b28d0d3cb05a7c3f

https://github.com/y-kamiya/ImageDream/commit/0855cbbed409a77edbbc3009adef3270e2591dfc

multiview diffusionの条件づけに使われるのはc2wレンダリングの条件に使われるのはmvpなので、TADA側の処理で使われるmvpのみ-90度回転させた。

結果

3dキャラクターの正面絵ということで、高品質で簡単に手に入るunityちゃんを使ってみた。

3d素材を落としてきてunity上で表示してスクショしたもの

これをImageDreamのmultiview diffusionに与えて2d生成した結果がこちら(batch=3)

3d生成した結果の4 view

パラメータの調整などはしてないためいろいろと崩れているが、全体的な特徴については学習できていそう。手の先にちょろっと紐みたいなものが出ているのは初期poseの手の名残。本来は初期poseとref imageのposeは合わせておく必要があるが、とりあえずそのままでやってもこのくらいにはなった。

multiview diffusionから出てくる画像は1 viewあたり256x256で小さく、細かい部分は描けないためちゃんとやるなら工夫が必要そう。また、text-3dではなかった問題としてref imageと学習条件の不一致による崩れがある。条件をちゃんと合わせれば精度よく学習できると思われるが合わせるのはなかなか大変そう。

コード全体はこちら
https://github.com/y-kamiya/TADA/compare/ee9baa44bae626ca6e6157e69a992823cdeb28a1..e1355f6cda7a099dcc365189ad4c7a4fe5f6ff81 https://github.com/y-kamiya/ImageDream/compare/26c3972e586f0c8d2f6c6b297aa9d792d06abebb...b2a7575168a19712c2bec897d8531e632fbf6bd7