# Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
本教學課程說明如何
.obj
檔案載入一個網格和紋理。確認已安裝 torch
和 torchvision
。如果未安裝 pytorch3d
,請使用以下儲存格安裝
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'
import os
import torch
import matplotlib.pyplot as plt
# Util function for loading meshes
from pytorch3d.io import load_objs_as_meshes, load_obj
# Data structures and functions for rendering
from pytorch3d.structures import Meshes
from pytorch3d.vis.plotly_vis import AxisArgs, plot_batch_individually, plot_scene
from pytorch3d.vis.texture_vis import texturesuv_image_matplotlib
from pytorch3d.renderer import (
look_at_view_transform,
FoVPerspectiveCameras,
PointLights,
DirectionalLights,
Materials,
RasterizationSettings,
MeshRenderer,
MeshRasterizer,
SoftPhongShader,
TexturesUV,
TexturesVertex
)
# add path for demo utils functions
import sys
import os
sys.path.append(os.path.abspath(''))
如果使用 Google Colab,請擷取用於繪製影像網格的 utility 檔案
!wget https://raw.githubusercontent.com/facebookresearch/pytorch3d/main/docs/tutorials/utils/plot_image_grid.py
from plot_image_grid import image_grid
或如果在本地執行,取消註解並執行以下儲存格
# from utils import image_grid
載入一個 .obj
檔案和其相關聯的 .mtl
檔案,並建立一個 Textures 和 Meshes 物件。
Meshes 是 PyTorch3D 中提供一個獨特的資料結構,用於處理不同大小網格的批次。
TexturesUV 是用於儲存網格的頂點 UV 和紋理貼圖的輔助資料結構。
Meshes 具有在渲染管線中使用的一些類別方法。
如果您使用 Google Colab 來執行此筆記本,請執行以下儲存格以擷取網格 obj 和紋理檔案,並將其儲存在路徑 data/cow_mesh
中:如果在本地執行,資料已在正確的路徑中。
!mkdir -p data/cow_mesh
!wget -P data/cow_mesh https://dl.fbaipublicfiles.com/pytorch3d/data/cow_mesh/cow.obj
!wget -P data/cow_mesh https://dl.fbaipublicfiles.com/pytorch3d/data/cow_mesh/cow.mtl
!wget -P data/cow_mesh https://dl.fbaipublicfiles.com/pytorch3d/data/cow_mesh/cow_texture.png
# 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"
obj_filename = os.path.join(DATA_DIR, "cow_mesh/cow.obj")
# Load obj file
mesh = load_objs_as_meshes([obj_filename], device=device)
plt.figure(figsize=(7,7))
texture_image=mesh.textures.maps_padded()
plt.imshow(texture_image.squeeze().cpu().numpy())
plt.axis("off");
PyTorch3D 有內建的方式,可以用 matplotlib 觀看紋理貼圖,以及貼圖中對應於頂點的點。還有一個 texturesuv_image_PIL 方法,可以取得一張相似的影像,該影像可以儲存到檔案中。
plt.figure(figsize=(7,7))
texturesuv_image_matplotlib(mesh.textures, subsample=None)
plt.axis("off");
PyTorch3D 中的渲染器是由一個 光柵器和一個 著色器 組成,每個都有一些子元件,例如**相機**(正交/透視)。我們在此初始化其中一些元件,並對其餘部分使用預設值。
在這個範例中,我們將先建立一個使用 透視相機、點光源並套用 Phong 著色 的 渲染器。然後,我們會學習如何透過模組化 API 來變更不同的元件。
# Initialize a camera.
# With world coordinates +Y up, +X left and +Z in, the front of the cow is facing the -Z direction.
# So we move the camera by 180 in the azimuth direction so it is facing the front of the cow.
R, T = look_at_view_transform(2.7, 0, 180)
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. We also set bin_size and max_faces_per_bin to None which ensure that
# the faster coarse-to-fine rasterization method is used. Refer to rasterize_meshes.py for
# explanations of these parameters. Refer to docs/notes/renderer.md for an explanation of
# the difference between naive and coarse-to-fine rasterization.
raster_settings = RasterizationSettings(
image_size=512,
blur_radius=0.0,
faces_per_pixel=1,
)
# Place a point light in front of the object. As mentioned above, the front of the cow is facing the
# -z direction.
lights = PointLights(device=device, location=[[0.0, 0.0, -3.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
)
)
光源位於物件前方,因此很明亮,而影像具有鏡面高光。
images = renderer(mesh)
plt.figure(figsize=(10, 10))
plt.imshow(images[0, ..., :3].cpu().numpy())
plt.axis("off");
我們可以透過呼叫 renderer
來將任意關鍵字引數傳遞給 rasterizer
/shader
,因此如果任何設定發生變更,就不需要重新初始化渲染器/
在這種情況下,我們可以直接更新光源的位置,並將其傳遞至呼叫渲染器。
由於只包含環境光源,沒有鏡面亮點,因此影像現在是暗的。
# Now move the light so it is on the +Z axis which will be behind the cow.
lights.location = torch.tensor([0.0, 0.0, +1.0], device=device)[None]
images = renderer(mesh, lights=lights)
plt.figure(figsize=(10, 10))
plt.imshow(images[0, ..., :3].cpu().numpy())
plt.axis("off");
# Rotate the object by increasing the elevation and azimuth angles
R, T = look_at_view_transform(dist=2.7, elev=10, azim=-150)
cameras = FoVPerspectiveCameras(device=device, R=R, T=T)
# Move the light location so the light is shining on the cow's face.
lights.location = torch.tensor([[2.0, 2.0, -2.0]], device=device)
# Change specular color to green and change material shininess
materials = Materials(
device=device,
specular_color=[[0.0, 1.0, 0.0]],
shininess=10.0
)
# Re render the mesh, passing in keyword arguments for the modified components.
images = renderer(mesh, lights=lights, materials=materials, cameras=cameras)
plt.figure(figsize=(10, 10))
plt.imshow(images[0, ..., :3].cpu().numpy())
plt.axis("off");
PyTorch3D API 的核心設計選擇之一,就是支援**所有組件的批次輸入**。渲染器和相關組件可以接受批次輸入,並**在一次順向傳遞中渲染一批次輸出影像**。我們現在將使用這項功能從許多不同的視點渲染網格。
# Set batch size - this is the number of different viewpoints from which we want to render the mesh.
batch_size = 20
# Create a batch of meshes by repeating the cow mesh and associated textures.
# Meshes has a useful `extend` method which allows us do this very easily.
# This also extends the textures.
meshes = mesh.extend(batch_size)
# Get a batch of viewing angles.
elev = torch.linspace(0, 180, batch_size)
azim = torch.linspace(-180, 180, batch_size)
# All the cameras helper methods support mixed type inputs and broadcasting. So we can
# view the camera from the same distance and specify dist=2.7 as a float,
# and then specify elevation and azimuth angles for each viewpoint as tensors.
R, T = look_at_view_transform(dist=2.7, elev=elev, azim=azim)
cameras = FoVPerspectiveCameras(device=device, R=R, T=T)
# Move the light back in front of the cow which is facing the -z direction.
lights.location = torch.tensor([[0.0, 0.0, -3.0]], device=device)
# We can pass arbitrary keyword arguments to the rasterizer/shader via the renderer
# so the renderer does not need to be reinitialized if any of the settings change.
images = renderer(meshes, cameras=cameras, lights=lights)
image_grid(images.cpu().numpy(), rows=4, cols=5, rgb=True)
如果你只想視覺化網格,你不需要使用可微分渲染器,相反地,我們支援用 Plotly 繪製網格。針對這些網格,我們使用 TexturesVertex 為渲染定義一個材質。plot_meshes 會使用每個網格物件的痕跡建立一個 Plotly 圖形。
verts, faces_idx, _ = load_obj(obj_filename)
faces = faces_idx.verts_idx
# Initialize each vertex to be white in color.
verts_rgb = torch.ones_like(verts)[None] # (1, V, 3)
textures = TexturesVertex(verts_features=verts_rgb.to(device))
# Create a Meshes object
mesh = Meshes(
verts=[verts.to(device)],
faces=[faces.to(device)],
textures=textures
)
# Render the plotly figure
fig = plot_scene({
"subplot1": {
"cow_mesh": mesh
}
})
fig.show()
# use Plotly's default colors (no texture)
mesh = Meshes(
verts=[verts.to(device)],
faces=[faces.to(device)]
)
# Render the plotly figure
fig = plot_scene({
"subplot1": {
"cow_mesh": mesh
}
})
fig.show()
# create a batch of meshes, and offset one to prevent overlap
mesh_batch = Meshes(
verts=[verts.to(device), (verts + 2).to(device)],
faces=[faces.to(device), faces.to(device)]
)
# plot mesh batch in the same trace
fig = plot_scene({
"subplot1": {
"cow_mesh_batch": mesh_batch
}
})
fig.show()
# plot batch of meshes in different traces
fig = plot_scene({
"subplot1": {
"cow_mesh1": mesh_batch[0],
"cow_mesh2": mesh_batch[1]
}
})
fig.show()
# plot batch of meshes in different subplots
fig = plot_scene({
"subplot1": {
"cow_mesh1": mesh_batch[0]
},
"subplot2":{
"cow_mesh2": mesh_batch[1]
}
})
fig.show()
對於批次,我們也可以使用 plot_batch_individually 來避免自行建構場景字典。
# extend the batch to have 4 meshes
mesh_4 = mesh_batch.extend(2)
# visualize the batch in different subplots, 2 per row
fig = plot_batch_individually(mesh_4)
# we can update the figure height and width
fig.update_layout(height=1000, width=500)
fig.show()
我們也可以在這兩個函數中修改軸參數和軸背景。
fig2 = plot_scene({
"cow_plot1": {
"cows": mesh_batch
}
},
xaxis={"backgroundcolor":"rgb(200, 200, 230)"},
yaxis={"backgroundcolor":"rgb(230, 200, 200)"},
zaxis={"backgroundcolor":"rgb(200, 230, 200)"},
axis_args=AxisArgs(showgrid=True))
fig2.show()
fig3 = plot_batch_individually(
mesh_4,
ncols=2,
subplot_titles = ["cow1", "cow2", "cow3", "cow4"], # customize subplot titles
xaxis={"backgroundcolor":"rgb(200, 200, 230)"},
yaxis={"backgroundcolor":"rgb(230, 200, 200)"},
zaxis={"backgroundcolor":"rgb(200, 230, 200)"},
axis_args=AxisArgs(showgrid=True))
fig3.show()
在本教學課程中,我們學習如何**載入**具材質紋理的網格從 obj 檔案,初始化一個 PyTorch3D 資料結構稱為**網格**,設定一個由**光柵化器**和**著色器**組成的**渲染器**,以及修改渲染管線的多個組件。我們也學習如何在 Plotly 圖形中渲染網格。