画像1枚から 3D を作ると、見えない横や背面はモデルの作り物になり、三面図(正面・横・背面)があるなら全部渡したくなります。ROCm で Pixal3D(C++ 版の trellis.cpp)を動かす技術検証では、その fork の pixal3d.cpp が --views で複数の視点を受け取れるので、同じ入力の正面だけから作った場合と比べました。

結論

  • 見えない面の輪郭が良くなりました(描画の IoU)。横は 0.912 → 0.972 と 0.882 → 0.958、背面は 0.963 → 0.969 と 0.954 → 0.982 です。正面はほぼ変わりません。
  • 横の絵を左右反転して反対の横に足した4面は、3面より良くなりませんでした。
  • 正面の細部が崩れることがあります。色が暗く濃く出るのは、1枚のときと同じです。
  • 出力が重くなります。面の目標が 100 万、atlas が 4096 に固定で、GLB は 55 MB 前後です。

試した環境

  • raven38/pixal3d.cpp の d1b49264(LighthausInc/pixal3d.cpp も同じ commit)。旗は trellis.cpp と同じ(下のコードのとおり)でビルドでき、210 段でした
  • cmake と ninja は、PyPI の wheel(cmake 3.31.10・ninja 1.13.0)を入れたビルド用の venv(下のコードの $HOME/venvs/build)の物を使いました
  • 複数視点用の flow は、fork の convert.py で GGUF にしました(8 部品で 59 秒、RSS 2.9 GiB、22 GB)。共有の5つ(DINOv3・NAF・3つのデコーダ)は、1枚用の一式への symlink で読めました
  • Radeon RX 7900 XT(VRAM 20 GB)、Ubuntu、ROCm 7.2.4。seed 42、1024
git clone https://github.com/raven38/pixal3d.cpp pixal3d.cpp
git -C pixal3d.cpp checkout d1b49264             # main が進むと別の版になるので固定する
cd pixal3d.cpp && git submodule update --init --depth 1 thirdparty/ggml
export PATH=$HOME/venvs/build/bin:/opt/rocm/bin:/opt/rocm/llvm/bin:$PATH ROCM_PATH=/opt/rocm HIP_PATH=/opt/rocm   # $HOME/venvs/build は cmake と ninja を入れた venv
cmake -S . -B build-hip -G Ninja -DCMAKE_BUILD_TYPE=Release \
  -DGGML_HIP=ON -DGGML_HIP_GRAPHS=OFF -DGPU_TARGETS=gfx1100 \
  -DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ \
  -DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++ -DCMAKE_PREFIX_PATH=/opt/rocm -DTRELLIS_WEBP=OFF
cmake --build build-hip --target trellis-cli trellis-test-proj-grid trellis-test-transforms-synth -j "$(nproc)"
build-hip/trellis-test-proj-grid --selftest       # 投影の式の自己検査(CPU だけ)

入力のそろえ方

三面図の各面を切り出して alpha を付け、どの面も同じ枠取りで 1024² にそろえました。同じ倍率・同じ高さの中心で、横の位置は各面の被写体の中心、一番大きい寸法が画面の 80% です。カメラは transforms.json で渡します。向きは Blender の流儀で、正面が方位 0、横が 90(キャラが画面の左を向く面。カメラは +X 側)、背面が 180 です。画角は 15°、原点を通る面で画面の半分の幅が 0.55 です。

つまり入力には、各面の alpha、面どうしで同じ倍率と床、分かっている方位、がそろっている必要があります。三面図なら、もともと満たしています。背景を塗りつぶして除くと、白い服や薄い靴底が背景に近い色のときに削れるおそれがあります(今回の3つでは削れませんでした)。

比べた値

題材は、自作の三面図(三面図 A)、別のキャラクターの六面図から取った正面・横・背面(三面図 B)、髪の部品の正面と背面の3つです。「1枚」は、同じ入力の正面だけを、同じエンジンの1枚用の重みで作ったものです。物差しは2つあります。「同じカメラの IoU」は fork の tools/silhouette_iou.py で測りました。入力のカメラで頂点を投影し、軸は 48 通りの総当たりで決めます。「描画の IoU」は、Blender で描いて外接枠の高さを 600 px にそろえ、重ねた値です。

題材・方式同じカメラの IoU 正面・横・背面(平均)描画の IoU 正面・横・背面所要VRAM
三面図 A・1枚0.964・0.915・0.936(0.938)0.975・0.912・0.963137 s5.6 GiB
三面図 A・3面0.957・0.966・0.955(0.959)0.974・0.972・0.969164 s6.6 GiB
三面図 A・4面0.961・0.964・0.945(0.957)0.970・0.969・0.973167 s6.6 GiB
三面図 B・1枚0.967・0.861・0.939(0.922)0.959・0.882・0.954169 s6.6 GiB
三面図 B・3面0.966・0.959・0.966(0.964)0.978・0.958・0.982177 s6.6 GiB
三面図 B・4面0.963・0.954・0.957(0.958)0.978・0.972・0.972181 s6.6 GiB
髪の部品・1枚0.975・—・0.953(0.964)—314 s7.0 GiB
髪の部品・正面と背面0.968・—・0.969(0.969)—347 s6.7 GiB

三面図を渡すと、三面図 A の横は 0.912 から 0.972、背面は 0.963 から 0.969、三面図 B の横は 0.882 から 0.958、背面は 0.954 から 0.982 に上がった

目で見ても違いがありました。1枚では作り物だった髪の背面のこぶが、入力どおりの重なった房になりました。三面図 B の背中のリュックは、1枚では暗い茶色に橙のしみが付いていましたが、辛子色になりました。所要の伸びは 5〜20% ほどで、VRAM は 7 GiB 以内でした。

横を左右反転して反対の横に足した4面は、3面より良くなりませんでした。髪の分け目などで、左右対称という仮定が外れるためです。

一方で、正面の細部が崩れることがありました。三面図 B の3面で、上着の前の合わせから見える白いシャツが暗くなりました(4面では薄くなる程度でした)。また、色が入力より暗く濃く出るのは1枚のときと同じなので、同じ色の補正が要ります。

細かな網目の重い彫像の下でたわむ展示台に、支えの柱を差し込む人たち

出力が重い

複数視点の方式では、面の目標が 100 万、atlas が 4096 に固定です。本家の inference_mv.py と同じ値で、CLI からは変えられません。GLB は 55 MB 前後になります。内側の見えない殻も大きく、三面図 A の3面では 69 成分・468,370 面と、全体の半分でした(1枚用の重みの出力にはありませんでした)。出力の向きは glTF の -Z が正面なので、Y 軸まわりに 180° 回して1枚の方式とそろえています。

後始末まで通した所要は、三面図 B の3面で 199.0 秒(pixal3d.cpp が 171.5 秒、後始末が 27 秒)でした。内側の殻 21 個・48,810 面を消して 936,926 面になり、色のずれは 0.1725 → 0.0176 です。髪の正面と背面は 367.3 秒で、991,422 面でした。

確かめていないこと

  • 正面のシャツが暗くなった理由。fork は各面の特徴を、遮蔽を見ずに平均します。そのため、正面からしか見えない所に背面のリュックの特徴が混じったのではないかと考えていますが、確かめていません。
  • 色の補正の背面への効き(目で見ただけです)。

おわりに

三面図があるなら、pixal3d.cpp の --views で、見えない横と背面の輪郭がはっきり良くなります。ただし 100 万面の重い GLB になるので、内側の殻の削除を一緒に用意しておくのがおすすめです。

※ pixal3d.cpp の引数・固定値・出力の向きは、執筆時点に試した commit での振る舞いで、いまは変わっている可能性があります。