この記事は?
trimeshを使う際の備忘録。trimeshは 3.21.0
を使用しています。
trimeshの使い方
カメラ座標系
trimeshはOpenGLをバックエンドで使用しているため、カメラ座標系は以下の図のような座標系を持っています。図はOpenGLの公式からお借りしました。
従って、COLMAPやpytorch3dなど別のカメラ座標系を持つライブラリと連携する場合、座標系を変換する必要があります(後述)。各ライブラリの座標表現は自分の過去記事にまとめたのでご参照ください。
モジュール解説
mesh
3Dオブジェクト本体となるモジュールです。メッシュは頂点と頂点同士の繋がり(メッシュの面、3つの頂点の組み合わせの集合)によって表現されます。これらは mesh.vertices
mesh.faces
などでアクセスできます。これらのアトリビュートはnumpyの配列と同様に扱えます。また、メッシュオブジェクトは色々なメソッドを備えているのでメモしておきます。
読み込み
mesh = trimesh.load_mesh(MESH_PATH)
保存
mesh.export(MESH_PATH)
メッシュの分割
メッシュオブジェクトが連結でない複数のパーツから構成される場合、split
メソッドでそれらをリスト化することができます。NeRFなどで学習するとfloating objectsが生成されることがありますが、split
して体積最大のメッシュを抽出するとこういうゴミを消せます。(ゴミの方が大きいと無理ですが。)
meshes = mesh.split(only_watertight=False)
# メッシュの面が最大のものを抽出する。体積最大を取り出したい場合は lambda x: x.volume でOK
mesh = max(meshes, key=lambda x: x.faces.shape[0])
メッシュの面を減らす
メッシュの面の数を適当に減らしたいときは mesh.simplify_quadric_decimation
で面を減らせます。メッシュの表面がガタガタしてて無駄に頂点数が増えてる時とかに使えると思います。
mesh = mesh.simplify_quadric_decimation(NUM_FACES)
メッシュの頂点座標を変換する
COLMAPなど別のカメラ座標系を持つライブラリで作成したメッシュをtrimeshで利用する場合、カメラ座標の変換が必要です。これは頂点座標の変換によって実現できます。頂点座標に対する変換は mesh.apply_transform
で実現できます。(頂点座標が変換されてもfaceは不変なので、変換はあくまで頂点座標に対してのみ適用されます。)この変換はhomogeneous transformationなので4x4行列を渡す必要があります。
# COLMAP -> OpenGL
permute_axis = np.diag([1, -1, -1, 1])
mesh.apply_transform(permute_axis)
メッシュの表示
mesh.show()
カメラ設定とカメラ姿勢の反映
メッシュをレンダリングする際にはカメラの内部パラメータと外部パラメータの設定を反映する必要があります。これは scene
メソッドによって実現できます。内部パラメータは camera
、外部パラメータは camera_transform
で渡します。ここで camera_transform
はcamera2worldで表現された4x4行列です。
focal = (FOCAL_LENGTH_X, FOCAL_LENGTH_Y)
resolution = (IMAGE_HEIGHT, IMAGE_WIDTH)
camera = trimesh.scene.cameras.Camera(
focal=focal,
resolution=resolution
)
scene = mesh.scene(
camera=camera,
camera_transform=RT,
)
光線とメッシュの交点を求める
mesh.ray.intersects_*
メソッドを使って求めます。光線の原点と光線の方向を渡すことで光線とメッシュの交点の面のインデックスや座標を求めることができます。光線の原点は全て同一の場合であっても光線方向の数だけ作成する必要があります。numpy.tile(origin[None], (num_rays, 1))
などでの数を調整する必要があります。
face_indices = mesh.ray.intersects_first(
ray_origins=origins, # (num_origins, 3)
ray_directions=directions, # (num_origins, 3)
)
また、pyembreeというライブラリを使うと光線とメッシュの交点計算が高速にできるようになります。RayMeshIntersector
インスタンス初期化時に交点を計算したいメッシュを渡すと交点計算系のメソッドを高速にやってくれます。
intersector = trimesh.ray.ray_pyembree.RayMeshIntersector(mesh, scale_to_box=False)
face_indices = intersector(
ray_origins=origins, # (num_origins, 3)
ray_directions=directions, # (num_origins, 3)
)