Gaussian Splatting + PanoHead で画像1枚から顔の3Dモデル(.ply)を生成する! (google colabあり)
Metachrosys で3D生成周りをお手伝いしています、落合陽一研の なま です。
この記事では、1枚の顔写真を入れるとその人の顔を好きな方向から見た画像が生成できる手法「PanoHead」と、ある物体をいくつかの視点から撮影した画像を入れると、その物体の .ply 形式 (※1) の3Dモデルを生成してくれる「Gaussian Splatting」を組み合わせて、顔画像1枚から顔の3Dモデルを生成する方法を Google Colab で作ったのでご紹介します。ついでに簡単に、これら2つの手法についても紹介します!
《 出来上がりイメージ 》
Google Colab はこちら⇩ (通して実行するのにかかる時間は約20分です)
【追記 (2024/02/16)】
AttributeError: module 'numpy' has no attribute 'long'. Did you mean: 'log'?
が出た場合は、以下を実行してください
!pip install numpy\<1.24.0
PanoHead とは
PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360° (CVPR 2023)
論文HP ⇨ https://sizhean.github.io/panohead
(本記事の図は論文HPより引用)
PanoHead は3DGAN の一種で、大量の人の顔画像写真を学習して様々な人の好きな方向から見た画像を生成します。少しだけ深ぼると、まずシード値 (※2) から「顔の前後」「顔の左右」「顔の上下」の特徴を圧縮した計3枚の特徴マップを生成し、3枚の特徴マップを空間全体に補間して、それを指定された方向から見た特徴画像をもとに自然画像を生成します (下図)。さらに、画像が与えられた場合にはこのシード値をうまく推定したり、必要に応じてその画像だけで画像生成モデルを追加学習することで、特定の人の好きな方向からの画像が生成できるようになります。
PanoHead は、厳密には高精度な3Dモデルを作って見せているわけではなく GAN できれいな画像を生成しているので、実際にレンダリングする際はそれなりのスペックのGPUが必要で、レンダリング時間も1/30秒くらいかかります。
PanoHead は EG3D という手法のプチ改良版のような立ち位置なので、より詳しく知りたい人はこちらの解説スライドも合わせてどうぞ!(手前味噌)
Gaussian Splatting とは
3D Gaussian Splatting for Real-Time Radiance Field Rendering (SIGGRAPH 2023)
論文HP ⇨ https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/
(本記事の図は論文HPより引用)
Gaussian Splatting は、あるシーンを撮影したざっくり数十枚~数百枚くらいの画像から、そのシーンを3Dで再構成してPC上にまるまる再現する手法です。同じような目的の技術として、フォトグラメトリー(1867?) ~ NeRF (2020) などがありましたが、Gaussian Splatting はフォトグラメトリーや NeRF よりもより詳細に、NeRFよりもより軽量なデータ形式でシーンを表現できるようになりました。
Gaussian Splatting は、はじめに画像セットの視覚的な特徴マッチングで得られるシーン上の疎な点群 (実際には Gaussian Splat というラグビーボールのような形の点の集まり) を用意し、画像セットを教師データとして生成誤差を小さくするように各点のサイズを変えたり点群を増やしたり減らしたりしながら表現力の高い点群に最適化していきます。
Colab コードの解説
大事なところだけ抜粋しています。PanoHead、Gaussian Splatting ともに、本家実装を colab 化した camenduru さんのコードを拝借しています。
(テスト用にこちらの画像を使わせていただきました)
PanoHead 部分
!mkdir /content/in /content/stage /content/output
# 左のファイルシステムから、in ディレクトリに画像 (.jpg) を1枚入れてください
!ls /content/in # 入っていることを確認
ここは画像をアップロードするだけですが、この colab で唯一操作が必要な部分なのでスクショを載せておきます
あとは ▷ の実行ボタンを押していくだけです。
!ls /content/stage # クロップされた画像ができていることを確認
!python projector_withseg.py --num-steps=300 --num-steps-pti=300 --outdir=/content/output --target_img=/content/stage --network=/content/PanoHead/models/easy-khair-180-gpc0.8-trans10-025000.pkl --idx 0
この部分で、シード値の推定と追加学習を続けて行っています。
!wget -O gen_videos_proj_withseg2.py https://gist.githubusercontent.com/naruya/2885444e87c8f3be54b88d1f6cda5b8a/raw/a79370e0055adc6c8ec05c271dde9a9152ee6321/gen_videos_proj_withseg.py
!python gen_videos_proj_withseg2.py --output=/content/output/easy-khair-180-gpc0.8-trans10-025000.pkl/0/PTI_render/post.mp4 --latent=/content/output/easy-khair-180-gpc0.8-trans10-025000.pkl/0/projected_w.npz --trunc 0.7 --network /content/output/easy-khair-180-gpc0.8-trans10-025000.pkl/0/fintuned_generator.pkl --cfg Head --w-frames 120
ここではシードの推定と追加学習の結果を用いて、顔画像をレンダリングしています。元のコードは顔周りをぐるっと一周する映像を生成するだけですが、この後 Gaussian Splatting で使うために一部コードを細工して、うまく背景を分離して顔部分だけを画像のままで保存しています。
(元のコードとの差分は こちら)
Gaussian Splatting 用のデータに変換
画像や映像が得られたので、あとはそれをもとに自動で3D化してくれる Luma.ai のようなサービスを使えばよいと思う方もいるかもしれませんが、大抵うまくいきません。PanoHead はあくまで画像生成モデルであり、生成された映像をよく見ると、ぼやけていたり、特徴が少なかったり、他の方向から見たときの三次元的な一貫性が低かったりするため、先ほどの "疎な点群" の抽出 (と同時にカメラ位置の推定) に失敗します (下図)。
そこで、疎な点群を抽出して初期値として使うことはやめて、Gaussian Splatting の「ランダムな点群から開始するモード」を使います。このモードにすると、撮ったデータがぼやけていたり、特徴が少なかったり、三次元的な一貫性が低かったりするときにも無理やり Gaussian Splatting を実行できていろいろ遊べます (恐らく)。
ただこのモードだと、特徴抽出をしないために Gaussian Splatting の学習に必要なカメラの姿勢情報が自動で推定できないので、手動で用意してあげます。幸い PanoHead では、原点に置かれた顔を中心にぐるっと一周カメラを回しただけで、ついでにカメラの姿勢情報も親切に保存されているので、これを Gaussian Splatting の形式に変換するだけです。
!wget -O eg3d_to_nerf.py https://gist.githubusercontent.com/naruya/438e59f69d27a53099f83b3940f92271/raw/857ce32022cee70eba637112e803657a6b58b977/eg3d_to_nerf.py
%cd /content/output/easy-khair-180-gpc0.8-trans10-025000.pkl/0/
!rm -rf sample
!mkdir sample
%cd sample
!mkdir train
!cp -r ../PTI_render/*.png train/
!cp ../PTI_render/post_trajectory.npy ./
!ls !python /content/PanoHead/eg3d_to_nerf.py --path ./
!cp -r train test !cp -r train val
!cp transforms_train.json transforms_test.json
!cp transforms_train.json transforms_val.json
!sed -i 's|./train/|./test/|g' transforms_test.json
!sed -i 's|./train/|./val/|g' transforms_val.json !pwd
eg3d_to_nerf.py はこのために用意した変換スクリプトで、このスクリプトの中では、PanoHead (EG3D系) の座標系から Gaussian Splatting (NeRFデータ形式) の座標系に変換することと、カメラの姿勢情報を json に変換することをやっています。
(コードは こちら)
Gaussian Splatting の部分
!python train.py -s /content/output/easy-khair-180-gpc0.8-trans10-025000.pkl/0/sample
あとは、作った画像とカメラ姿勢のデータセットで Gaussian Splatting を学習するだけです。先ほどの変換部分で NeRF Synthetic Dataset 形式のデータにしてあるので、自動で「ランダムな点群から開始するモード」に切り替わります。
学習結果は gaussian-splatting/output/{model}/point_cloud/iteration_7000/ point_cloud.ply に保存されるので、これをダウンロードしてください。Iteration_30000/point_cloud.ply もありますが、経験的には途中の 7000 の方が綺麗です。
ビューアーで見てみる
Gaussian Splatting で生成された .ply モデルのビューアーには、このブラウザアプリがおすすめです
https://playcanvas.com/super-splat
左下の「ファイルを選択」からアップロードすると、何やら点群全体が青いですが、「Splat Size」を 0 にすると直ります。(青くすると後述するような点群を選択するときに見やすくなります。)
また向きが上下逆さですが、左上の「Rotate」アイコンを押して、赤/緑/青のどれかの円をドラッグすると回転できます。
最後に、上下に広がる雲のような点群は、「Rect」や「Brush」で選択した後、「Delete Selected Splats」で消すと綺麗になります。(この時は Splat Size を大きくすると見やすくなります) この雲はランダムな初期化の名残です。
( Esc キーで「Rotate」や「Rect」/「Brush」モードを解除できます)
SuperSplat の操作の様子⇩
以上!
おまけ (他の生成サービスとの比較)
うーん微妙ですね…!今のところ、ほとんどの3D生成モデルは大量の3Dモデルも使って学習しているからかおもちゃっぽい雰囲気になる傾向がある気がします。それに比べて PanoHead のような人の顔の写真だけで学習した生成モデルを使うと、ちゃんとフォトリアルになってくれて良いですね。
Metachorsysでは仲間を募集しています!
Metachrosys は「物理に依存しない新たな装いのあたりまえをつくる」というミッションの元、3DCG・生成AIを用いたオンライン試着や、3DCGのデジタルサンプルを活用したアパレルサプライチェーンDX、メタバース空間における新たなファッション表現を追求しています。 3D生成の研究開発メンバーやプロダクトマネージャー、デザイナーなど各ポジション募集中ですので、 ぜひ弊社HP、X (Twitter)からご連絡お待ちしています。
HP: https://metachrosys.com/
X (Twitter): https://twitter.com/metachrosys
※1: たぶん一般的な .ply というわけではなく gaussian splatting 形式の .ply なので、普通の3Dアプリで開くと色が付いていなかったりします。そのため記事中で紹介したようなビューアーで見る必要があります。
※2: シード値と書きましたが、実際には latent code と呼ばれるベクトルです。
この記事が気に入ったらサポートをしてみませんか?