メインコンテンツまでスキップ

usdzipをPythonで扱う話

USD AdventCalendar2022 23 日目は、python で usdz を扱う話です。

usdz とは、 usd ファイル、画像、オーディオデータを zip ファイルでパッケージングしたファイルフォーマットです。 usd 単体であれば、複数のファイルから合成されたステージであったとしても Flatten して単一ファイルに固めることは可能ですが、 それだと画像ファイルなどは単一ファイルに含めることができません。 ですが、usdz を使用すれば単一ファイルで 画像やオーディオファイルまで含んだデータを保持することができます。

サンプルをみてみる​

まずはサンプルを見てみます。

https://github.com/usd-wg/assets

USD の主にスキーマのテスト用アセットなどを公開しているASWF USD WGにある、 full_assets/ElephantWithMonochord を見てみます。

:fa-external-link: Usdz File Format Specification

このサンプルには、usd 本体以外に、テクスチャとオーディオデータも含まれています。

オーディオデータを見ると filePath に mp3 が指定されていたり、

Texture には png が使用されているのがわかります。

usdz は zip ファイルなので、拡張子を zip に変更すると中身を見ることができます。

zip ファイルをの中をみると、 usdc (usd バイナリー) が置かれています。

0 の中には、テクスチャとオーディオファイルが入っています。

このように、usdz にすることで 3DCG のアセットデータだけではなく、テクスチャ等も含めて 1 つのファイルとして扱えるようになりますので Apple の AR Quick Look のように、AR で表示するデータなどのフォーマットとしても使用されています。

作る​

usdzip を使った作り方等は usdzip の使い方 にまとめてありますので そちらを参照してください。

from pxr import Usd,Ar,Sdf
import os.path

with Usd.ZipFileWriter.CreateNew("D:/sample.usdz") as usdwriter:
for root,dirs,files in os.walk("D:/usdzSample"):
for i in files:
usdwriter.AddFile(os.path.join(root,i))

作る場合は Python でも簡単にできます。 UsdZipFileWriter というクラスが用意されていますので、これを利用して usdz に入れたいファイルを AddFile することで、 usdz 化できます。

読み込む​

usdz の場合は、usdc や usda と同じように UsdStage で開くことができます。

stage = Usd.Stage.Open("D:/SoC-ElephantWithMonochord.usdz")

扱いは他と同じですね。

中身を確認する​

usd ファイルとしてステージを開くのであれば、 UsdStageOpen で良いですが そうではなく zip に含まれるファイルを取得したいこともあります。 その場合は、UsdZipFile を使用します。

zipfile = Usd.ZipFile.Open("D:/SoC-ElephantWithMonochord.usdz")

for f in zipfile.GetFileNames():
print(f)

GetFileNames で、usdz に含まれているファイルを確認すると zip 以下の相対パスで、ファイル名を確認できます。

from PIL import Image,ImageOps
import io

img = zipfile.GetFile("0/Monochord_diff.png")
i = Image.open(io.BytesIO(img))
i

zipfile の GetFile を使用すると、zipfile 内のバイナリデータを取得できます。 ので、これを利用して、Pillow で開き jupyternotebook で画像を表示することなどができます。

このように、UsdZipFile を使用して開けば zip に含まれている画像ファイルを Pillow の Image で取得したりもできるので 画像付きパラメーターファイルとして usdz を使用するみたいなことも可能になります。

:fa-external-link: 参考 usdzip の中身を読んでみる

Stage を開いて、テクスチャパスを確認してみる​

UsdZipFile を使用して zip の中身を確認したり取得したりできるのはわかりましたが 最後に、Stage からテクスチャのパスがどのようになっているのかみてみます。

prim = stage.GetPrimAtPath("/SoC_ElephantWithMonochord/Materials/Elefant_Mat_68050/PreviewSurface/_MainTex")
texPath = prim.GetAttribute("inputs:file").Get()

print(texPath.resolvedPath)

テクスチャの inputs:file アトリビュートは AssetPath タイプで取得できます。 AssetPath は、Resolver によって解決されるもので

texPath.path

とすれば

未解決の、ファイルに記述されたテキストが取得され、

texPath.resolvedPath

だと、

最終的に解決済のファイルパスを取得できます。 usdz の場合だと、zip 内にファイルが存在しているので usdz_相対パス のような形で、resolvedPath を得ることができます。

制約事項​

このように、複数のファイルをまとめて扱ったりできるので非常に便利な usdz ですが いくつかの制約事項があります。 それは、readonly であることです。

内部のファイルを編集したい場合は、zip 内のファイルを一度解凍したうえで 編集して、再度 zip 化する必要があります。 編集中のデータというよりも、作成済のアセットデータを 配布したり、レイアウトに使用したりする場合のデータとして扱います。

内包できるファイルタイプ​

usdz に入れることができるのは、 usda usdc usd ファイルであり Alembic 等のような FileFormatPlugin でロードする物は入れることができません。

また、画像ファイルは png jpg、埋め込みオーディオファイルは m4a mp3 wav で、扱えるファイルに制限があるのに注意が必要です。

アセット内のファイルパス​

基本、usdz は zip 化するという構造上 単一ファイルとして独立した、カプセル化を行う必要があります。 つまりは、usdz 内に含まれる usd のシーンデータにあるパスは usdz 内に含まれるアセットデータへのアンカーパス( ../ 等から始まるパス) にすることで、カプセル化できます。