TADAは元々text-3dの手法でimage条件付けの実装は含まれていない。 しかし、それだとstable diffusionが知ってるキャラクターしか作れないため、画像からavatar生成ができるよう実装を加えてみたのでメモ。
guidanceの部分でreference imageを元にした生成ができれば元と同じようにsdsを用いて3d生成が可能と考えられる。reference imageを元にした画像生成はいろいろ方法があるが、今回はimage条件付け可能なmultivew diffusionであるImageDreamを利用することにした。
ImageDreamについてはこちらに書いた
ImageDreamの調査 - MEMOcho-
実装調査
ImageDreamはthreestudio上に実装されており、guidance部分はここ
ImageDreamもsdsによって3d生成する形なので、基本的にはこれをまるごと使うことが可能なはず。ただしmultiview diffusionであり、diffusionの推論を行う際にはImageDreamの生成条件に合わせたcamera poseの情報がcontextとして必要となる。
そして、camera poseを作っているのがこちら
datasetとしてcamera poseを作り、その条件で3d表現からのレンダリングを実行、レンダリングした画像とそのcamera poseをguidanceのinputに与えてsdsという流れ。
TADA側も流れとしては同様になっている。ということで、datasetの部分もImageDreamのものに差し替えてしまうのが最も手間がかからなさそう。
実装
座標系合わせ
ImageDream側のdatasetとguidance用のclassを、TADAの学習時の処理から使うようにしてみたところ、レンダリング結果がこんな状態になる
これは座標系の問題
- TADAはopengl座標系
- ImageDreamはblender座標系
- おそらくmultiview diffusionの学習用datasetの作成時にblenderを使っていたため
- ここでblender座標系に変換する処理を入れようとしている
- https://github.com/bytedance/ImageDream/blob/26c3972e586f0c8d2f6c6b297aa9d792d06abebb/threestudio/models/guidance/multiview_diffusion_guidance.py#L84-L85
具体的には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
ちなみにopenglとblender座標系の違いを表にまとめていてわかりやすかった
BlenderとUnityの座標系の違いを治す|KitanoMinami
角度のズレ
座標系は合わせることができたが、以下の2つの画像が水平方向に90度ズレる
- smplxをレンダリングした画像
- multiview diffusionによる生成結果
レンダリングや生成条件として渡す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