PyTorch3D

PyTorch3D

  • ドキュメント
  • チュートリアル
  • API
  • GitHub

›

チュートリアル

  • 概要

3Dオペレーター

  • メッシュのフィット
  • バンドル調整

レンダリング

  • テクスチャ付きメッシュのレンダリング
  • DensePoseメッシュのレンダリング
  • 色付き点群のレンダリング
  • レンダリングによるテクスチャ付きメッシュのフィット
  • 微分可能レンダリングによるカメラ位置の最適化
  • レイマーチングによるボリュームのフィット
  • レイマーチングによる簡略化されたNeRFのフィット

データローダー

  • ShapeNetCoreとR2N2のデータローダー

Implicitron

  • implicitronを使用したカスタムボリューム関数のトレーニング
  • Implicitron設定システムの詳細
Google Colabで実行
チュートリアルJupyter Notebookをダウンロード
チュートリアルソースコードをダウンロード
In [ ]
# Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.

DensePoseのレンダリング¶

DensePoseは、密な人体ポーズ表現を指します: https://github.com/facebookresearch/DensePose。このチュートリアルでは、PyTorch3DでDensePoseデータを使用する例を提供します。

このチュートリアルでは、以下の方法を示します。

  • denseposeの.matおよび.pklファイルからメッシュとテクスチャをロードする
  • レンダラーをセットアップする
  • メッシュをレンダリングする
  • 照明やカメラの位置などのレンダリング設定を変更する

モジュールのインポート¶

torchとtorchvisionがインストールされていることを確認してください。 pytorch3dがインストールされていない場合は、次のセルを使用してインストールしてください。

In [ ]
import os
import sys
import torch
need_pytorch3d=False
try:
    import pytorch3d
except ModuleNotFoundError:
    need_pytorch3d=True
if need_pytorch3d:
    if torch.__version__.startswith("2.2.") and sys.platform.startswith("linux"):
        # We try to install PyTorch3D via a released wheel.
        pyt_version_str=torch.__version__.split("+")[0].replace(".", "")
        version_str="".join([
            f"py3{sys.version_info.minor}_cu",
            torch.version.cuda.replace(".",""),
            f"_pyt{pyt_version_str}"
        ])
        !pip install fvcore iopath
        !pip install --no-index --no-cache-dir pytorch3d -f https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/{version_str}/download.html
    else:
        # We try to install PyTorch3D from source.
        !pip install 'git+https://github.com/facebookresearch/pytorch3d.git@stable'
In [ ]
# We also install chumpy as it is needed to load the SMPL model pickle file.
!pip install chumpy
In [ ]
import os
import torch
import matplotlib.pyplot as plt
import numpy as np

# libraries for reading data from files
from scipy.io import loadmat
from PIL import Image
import pickle

# Data structures and functions for rendering
from pytorch3d.structures import Meshes
from pytorch3d.renderer import (
    look_at_view_transform,
    FoVPerspectiveCameras, 
    PointLights, 
    DirectionalLights, 
    Materials, 
    RasterizationSettings, 
    MeshRenderer, 
    MeshRasterizer,  
    SoftPhongShader,
    TexturesUV
)

# add path for demo utils functions 
import sys
import os
sys.path.append(os.path.abspath(''))

SMPLモデルのロード¶

SMPLモデルをダウンロードする¶

  • https://smpl.is.tue.mpg.de/download.phpにアクセスし、サインアップしてください。
  • Pythonユーザー向けのSMPLをダウンロードし、解凍してください。
  • 男性テンプレートファイル 'models/basicModel_m_lbs_10_207_0_v1.0.0.pkl' をdata/DensePose/フォルダにコピーしてください。
    • ファイル名を 'smpl_model.pkl' に変更するか、下記でコメントされている文字列をリネームしてください。

Google Colabを使用してこのノートブックを実行する場合は、次のセルを実行してテクスチャとUV値を取得し、正しいパスに保存してください。

In [ ]
# Texture image
!wget -P data/DensePose https://raw.githubusercontent.com/facebookresearch/DensePose/master/DensePoseData/demo_data/texture_from_SURREAL.png

# UV_processed.mat
!wget https://dl.fbaipublicfiles.com/densepose/densepose_uv_data.tar.gz
!tar xvf densepose_uv_data.tar.gz -C data/DensePose
!rm densepose_uv_data.tar.gz

テクスチャUVデータとSMPLデータをロードし、データ値と形式を修正するためのいくつかの処理を行います。

In [ ]
# Setup
if torch.cuda.is_available():
    device = torch.device("cuda:0")
    torch.cuda.set_device(device)
else:
    device = torch.device("cpu")
    
# Set paths
DATA_DIR = "./data"
data_filename = os.path.join(DATA_DIR, "DensePose/UV_Processed.mat")
tex_filename = os.path.join(DATA_DIR,"DensePose/texture_from_SURREAL.png")
# rename your .pkl file or change this string
verts_filename = os.path.join(DATA_DIR, "DensePose/smpl_model.pkl")


# Load SMPL and texture data
with open(verts_filename, 'rb') as f:
    data = pickle.load(f, encoding='latin1') 
    v_template = torch.Tensor(data['v_template']).to(device) # (6890, 3)
ALP_UV = loadmat(data_filename)
with Image.open(tex_filename) as image:
    np_image = np.asarray(image.convert("RGB")).astype(np.float32)
tex = torch.from_numpy(np_image / 255.)[None].to(device)

verts = torch.from_numpy((ALP_UV["All_vertices"]).astype(int)).squeeze().to(device) # (7829,)
U = torch.Tensor(ALP_UV['All_U_norm']).to(device) # (7829, 1)
V = torch.Tensor(ALP_UV['All_V_norm']).to(device) # (7829, 1)
faces = torch.from_numpy((ALP_UV['All_Faces'] - 1).astype(int)).to(device)  # (13774, 3)
face_indices = torch.Tensor(ALP_UV['All_FaceIndices']).squeeze()  # (13774,)
In [ ]
# Display the texture image
plt.figure(figsize=(10, 10))
plt.imshow(tex.squeeze(0).cpu())
plt.axis("off");

DensePoseでは、ボディメッシュは24個のパーツに分割されています。テクスチャ画像では、24個のパーツがボディパーツごとに個別の(200, 200)画像に分割されていることがわかります。DensePoseの慣例では、メッシュ内の各面はボディパーツに関連付けられています(上記のface_indicesテンソルで指定)。各面の頂点UV値(範囲[0, 1])は、メッシュ面が対応するボディパーツの(200, 200)サイズのテクスチャマップに固有です。これらをテクスチャマップ全体で直接使用することはできません。関連付けられた面が対応するボディパーツに応じて、頂点UV値をオフセットする必要があります。

In [ ]
# Map each face to a (u, v) offset
offset_per_part = {}
already_offset = set()
cols, rows = 4, 6
for i, u in enumerate(np.linspace(0, 1, cols, endpoint=False)):
    for j, v in enumerate(np.linspace(0, 1, rows, endpoint=False)):
        part = rows * i + j + 1  # parts are 1-indexed in face_indices
        offset_per_part[part] = (u, v)

U_norm = U.clone()
V_norm = V.clone()

# iterate over faces and offset the corresponding vertex u and v values
for i in range(len(faces)):
    face_vert_idxs = faces[i]
    part = face_indices[i]
    offset_u, offset_v = offset_per_part[int(part.item())]
    
    for vert_idx in face_vert_idxs:   
        # vertices are reused, but we don't want to offset multiple times
        if vert_idx.item() not in already_offset:
            # offset u value
            U_norm[vert_idx] = U[vert_idx] / cols + offset_u
            # offset v value
            # this also flips each part locally, as each part is upside down
            V_norm[vert_idx] = (1 - V[vert_idx]) / rows + offset_v
            # add vertex to our set tracking offsetted vertices
            already_offset.add(vert_idx.item())

# invert V values
V_norm = 1 - V_norm
In [ ]
# create our verts_uv values
verts_uv = torch.cat([U_norm[None],V_norm[None]], dim=2) # (1, 7829, 2)

# There are 6890 xyz vertex coordinates but 7829 vertex uv coordinates. 
# This is because the same vertex can be shared by multiple faces where each face may correspond to a different body part.  
# Therefore when initializing the Meshes class,
# we need to map each of the vertices referenced by the DensePose faces (in verts, which is the "All_vertices" field)
# to the correct xyz coordinate in the SMPL template mesh.
v_template_extended = v_template[verts-1][None] # (1, 7829, 3)

テクスチャ付きメッシュの作成¶

Meshes は、PyTorch3Dで提供される、異なるサイズのメッシュのバッチを操作するための独自データ構造です。

TexturesUV は、メッシュの頂点uvとテクスチャマップを保存するための補助データ構造です。

In [ ]
texture = TexturesUV(maps=tex, faces_uvs=faces[None], verts_uvs=verts_uv)
mesh = Meshes(v_template_extended, faces[None], texture)

レンダラーの作成¶

In [ ]
# Initialize a camera.
# World coordinates +Y up, +X left and +Z in.
R, T = look_at_view_transform(2.7, 0, 0) 
cameras = FoVPerspectiveCameras(device=device, R=R, T=T)

# Define the settings for rasterization and shading. Here we set the output image to be of size
# 512x512. As we are rendering images for visualization purposes only we will set faces_per_pixel=1
# and blur_radius=0.0. 
raster_settings = RasterizationSettings(
    image_size=512, 
    blur_radius=0.0, 
    faces_per_pixel=1, 
)

# Place a point light in front of the person. 
lights = PointLights(device=device, location=[[0.0, 0.0, 2.0]])

# Create a Phong renderer by composing a rasterizer and a shader. The textured Phong shader will 
# interpolate the texture uv coordinates for each vertex, sample from a texture image and 
# apply the Phong lighting model
renderer = MeshRenderer(
    rasterizer=MeshRasterizer(
        cameras=cameras, 
        raster_settings=raster_settings
    ),
    shader=SoftPhongShader(
        device=device, 
        cameras=cameras,
        lights=lights
    )
)

SMPLモデルとテクスチャマップから作成したテクスチャ付きメッシュをレンダリングします。

In [ ]
images = renderer(mesh)
plt.figure(figsize=(10, 10))
plt.imshow(images[0, ..., :3].cpu().numpy())
plt.axis("off");

身体の異なるビューと照明¶

レンダリングパイプラインでは、他にも多くの設定を変更できます。ここでは、

  • カメラの視角を変更します
  • 点光源の位置を変更します
In [ ]
# Rotate the person by increasing the elevation and azimuth angles to view the back of the person from above. 
R, T = look_at_view_transform(2.7, 10, 180)
cameras = FoVPerspectiveCameras(device=device, R=R, T=T)

# Move the light location so the light is shining on the person's back.  
lights.location = torch.tensor([[2.0, 2.0, -2.0]], device=device)

# Re render the mesh, passing in keyword arguments for the modified components.
images = renderer(mesh, lights=lights, cameras=cameras)
In [ ]
plt.figure(figsize=(10, 10))
plt.imshow(images[0, ..., :3].cpu().numpy())
plt.axis("off");

結論¶

このチュートリアルでは、DensePoseモデルとuvデータからテクスチャ付きメッシュを構築し、レンダラーを初期化し、レンダリングされたメッシュの視角と照明を変更する方法を学びました。

pytorch3d
Facebook Open Source
Copyright © 2024 Meta Platforms, Inc
法務:プライバシー利用規約