# Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
本教學將說明如何:
torch.utils.data.DataLoader
。請確認已安裝 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 numpy as np
import torch
from pytorch3d.datasets import (
R2N2,
ShapeNetCore,
collate_batched_meshes,
render_cubified_voxels,
)
from pytorch3d.renderer import (
OpenGLPerspectiveCameras,
PointLights,
RasterizationSettings,
TexturesVertex,
look_at_view_transform,
)
from pytorch3d.structures import Meshes
from torch.utils.data import DataLoader
# add path for demo utils functions
import sys
import os
sys.path.append(os.path.abspath(''))
如果使用 Google Colab,請擷取用於繪製影像格線的工具檔案
!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
若尚未下載 ShapeNetCore 資料集,請依照此處的說明完成下載步驟:https://www.shapenet.org/。ShapeNetCore 是 ShapeNet 資料集的子集。在 PyTorch3D 中,我們支援版本 1 (57 類別) 和版本 2 (55 類別)。
然後變更下方的 SHAPENET_PATH
路徑,以指向您當地 ShapeNetCore 資料集資料夾的路徑。
# Setup
if torch.cuda.is_available():
device = torch.device("cuda:0")
torch.cuda.set_device(device)
else:
device = torch.device("cpu")
SHAPENET_PATH = ""
shapenet_dataset = ShapeNetCore(SHAPENET_PATH)
R2N2 資料集可使用此處的說明下載:http://3d-r2n2.stanford.edu/。查看 ShapeNetRendering
和 ShapeNetVox32
的連結。R2N2 資料集包含 13 個類別,為 ShapeNetCore v.1 資料集的子集。R2N2 資料集也包含每一個物件各自 24 個渲染影像,以及體素模型。
然後變更下方的 R2N2_PATH
和 SPLITS_PATH
,讓路徑分別指向您當地 R2N2 資料集資料夾路徑及分割檔案路徑。在此,我們將載入 R2N2 的 train
分割,並要求回傳每個模型的體素。
R2N2_PATH = ""
SPLITS_PATH = "None"
r2n2_dataset = R2N2("train", SHAPENET_PATH, R2N2_PATH, SPLITS_PATH, return_voxels=True)
我們可以索引載入的資料集來擷取模型。對於 ShapeNetCore 和 R2N2,我們可以檢查此模型屬於的類別(以同義詞集識別碼形式表示,等同於 ImageNet 的 API 中描述的 wnid:http://image-net.org/download-API),其模型識別碼,以及其頂點和面。
shapenet_model = shapenet_dataset[6]
print("This model belongs to the category " + shapenet_model["synset_id"] + ".")
print("This model has model id " + shapenet_model["model_id"] + ".")
model_verts, model_faces = shapenet_model["verts"], shapenet_model["faces"]
我們可以使用其頂點和面來形成 Meshes
物件,此物件是 PyTorch3D 用於處理批次網格的資料結構。
model_textures = TexturesVertex(verts_features=torch.ones_like(model_verts, device=device)[None])
shapenet_model_mesh = Meshes(
verts=[model_verts.to(device)],
faces=[model_faces.to(device)],
textures=model_textures
)
使用 R2N2 時,我們可以進一步檢查 R2N2 的原始渲染影像。例如,倘若我們想要查看 R2N2 資料集中第 11 個物件的第二及第三個檢視,可以執行下列步驟
r2n2_renderings = r2n2_dataset[10,[1,2]]
image_grid(r2n2_renderings.numpy(), rows=1, cols=2, rgb=True)
torch.utils.data.DataLoader
處理資料集¶訓練深度學習模型通常需要傳入批次的輸入。PyTorch 的 torch.utils.data.DataLoader
有助於執行此動作。PyTorch3D 提供一個函數 collate_batched_meshes
,用於將輸入網格分組成單一 Meshes
物件,此物件代表批次。然後,PyTorch3D 操作可以進一步根據需要,直接使用 Meshes
資料結構,作為深度學習模型的一部分(例如 graph_conv
)。
對 R2N2 來說,如果批次中的所有模型都有相同數量的視圖,視圖、旋轉矩陣、平移矩陣、內表示矩陣和體素也會堆疊成批次張量。
注意:R2N2 的 val
分割中所有模型都有 24 個視圖,但有 8 個模型將它們的 24 個視圖分割成 train
和 test
分割,這種情況下 collate_batched_meshes
只能將矩陣、視圖和體素合併成清單。但是,可以透過設定 return_all_views = False
,並只載入每個模型的一個視圖來避免這種情況。
batch_size = 12
r2n2_single_view = R2N2("train", SHAPENET_PATH, R2N2_PATH, SPLITS_PATH, return_all_views=False, return_voxels=True)
r2n2_loader = DataLoader(r2n2_single_view, batch_size=batch_size, collate_fn=collate_batched_meshes)
讓我們視覺化批次中的所有視圖(每個模型一個)
it = iter(r2n2_loader)
r2n2_batch = next(it)
batch_renderings = r2n2_batch["images"] # (N, V, H, W, 3), and in this case V is 1.
image_grid(batch_renderings.squeeze().numpy(), rows=3, cols=4, rgb=True)
ShapeNetCore
和 R2N2
數據載入器都有自訂的 render
函數,支援透過 PyTorch3D 的可微分渲染器實作,並指定其模型 ID、類別或索引來渲染模型。
# Rendering settings.
R, T = look_at_view_transform(1.0, 1.0, 90)
cameras = OpenGLPerspectiveCameras(R=R, T=T, device=device)
raster_settings = RasterizationSettings(image_size=512)
lights = PointLights(location=torch.tensor([0.0, 1.0, -2.0], device=device)[None],device=device)
首先,我們將嘗試依模型 ID 渲染三個模型
images_by_model_ids = shapenet_dataset.render(
model_ids=[
"13394ca47c89f91525a3aaf903a41c90",
"14755c2ee8e693aba508f621166382b0",
"156c4207af6d2c8f1fdc97905708b8ea",
],
device=device,
cameras=cameras,
raster_settings=raster_settings,
lights=lights,
)
image_grid(images_by_model_ids.cpu().numpy(), rows=1, cols=3, rgb=True)
假設我們要渲染資料集中的前三個模型,我們可以依索引渲染模型
images_by_idxs = shapenet_dataset.render(
idxs=list(range(3)),
device=device,
cameras=cameras,
raster_settings=raster_settings,
lights=lights,
)
image_grid(images_by_idxs.cpu().numpy(), rows=1, cols=3, rgb=True)
或者,如果我們對特定模型沒有興趣,但想從一些特定類別中查看隨機模型,我們可以透過指定 categories
和 sample_nums
來執行此操作。例如,如果我們想從類別「水龍頭」渲染 2 個模型,並從類別「椅子」渲染 3 個模型,我們可以執行下列操作
images_by_categories = shapenet_dataset.render(
categories=["faucet", "chair"],
sample_nums=[2, 3],
device=device,
cameras=cameras,
raster_settings=raster_settings,
lights=lights,
)
image_grid(images_by_categories.cpu().numpy(), rows=1, cols=5, rgb=True)
如果我們對特定類別沒有興趣,而只是想從整個資料集中渲染一些隨機模型,我們可以在 sample_nums
中設定要渲染的模型數量,而不指定任何 categories
random_model_images = shapenet_dataset.render(
sample_nums=[3],
device=device,
cameras=cameras,
raster_settings=raster_settings,
lights=lights,
)
image_grid(random_model_images.cpu().numpy(), rows=1, cols=5, rgb=True)
我們可以以與上述渲染 ShapeNetCore 模型相同的方式渲染 R2N2 模型。此外,我們還可以將 R2N2 模型渲染成與資料集中原始渲染相同的定向。為此,我們將使用 R2N2 自訂的 render
函數和不同型式的 PyTorch3D 相機,稱為 BlenderCamera
。
在此範例中,我們將第七個模型渲染成與其第二個和第三個視圖相同的定向。首先,我們將擷取 R2N2 的原始渲染,以便與結果比較。
original_rendering = r2n2_dataset[6,[1,2]]["images"]
image_grid(original_rendering.numpy(), rows=1, cols=2, rgb=True)
接下來,我們將視覺化 PyTorch3D 的渲染
r2n2_oriented_images = r2n2_dataset.render(
idxs=[6],
view_idxs=[1,2],
device=device,
raster_settings=raster_settings,
lights=lights,
)
image_grid(r2n2_oriented_images.cpu().numpy(), rows=1, cols=2, rgb=True)
R2N2 數據載入器也會傳回模型的體素。我們可以使用 R2N2 的 render_vox_to_mesh
函數來視覺化它們。這會將體素立方化成 Meshes 物件,然後再渲染。
在此範例中,我們將視覺化資料集中第 10 個模型,定向與其第二個和第三個視圖相同。首先,我們將擷取 R2N2 的原始渲染,以便與結果比較。
r2n2_model = r2n2_dataset[9,[1,2]]
original_rendering = r2n2_model["images"]
image_grid(original_rendering.numpy(), rows=1, cols=2, rgb=True)
接著,我們將體素傳遞給 render_vox_to_mesh
vox_render = render_cubified_voxels(r2n2_model["voxels"], device=device)
image_grid(vox_render.cpu().numpy(), rows=1, cols=2, rgb=True)