- 人工智能
- 深度学习
- 计算机视觉
- 图形学
【免费下载链接】pytorch3d
PyTorch3D is FAIR's library of reusable components for deep learning with 3D data
本文是 PyTorch3D 官方数据加载笔记(docs/notes/datasets.md)的完整技术指南,系统讲解两大常用 3D 数据集ShapeNetCore与R2N2的加载、批处理与可微渲染实践。读者将掌握ShapeNetCore与R2N2两个数据加载类的构造参数、__getitem__返回的数据字典结构、配套的collate_fn批处理函数,以及内置render函数的四种模型选取方式,并了解其与 PyTorch3DMeshes结构与可微渲染管线的衔接原理。
在 PyTorch3D 的三维深度学习工作流中,数据加载是连接原始 CAD 模型与网格操作、渲染、损失计算的第一步。PyTorch3D 在 pytorch3d/datasets/ 目录下为两个最常用的公开数据集提供了开箱即用的torch.utils.data.Dataset实现:ShapeNetCore(位于 pytorch3d/datasets/shapenet/shapenet_core.py)与R2N2(位于 pytorch3d/datasets/r2n2/r2n2.py)。二者共享一个抽象基类ShapeNetBase(位于 pytorch3d/datasets/shapenet_base.py),该基类实现了纹理加载、渲染参数解析、类别随机采样等公共逻辑。
ShapeNetCore 数据加载器
数据集背景与版本差异
ShapeNet 是一个包含三维 CAD 模型的大型数据集,ShapeNetCore 是其中的一个子集,可从 ShapeNet 官网申请下载。ShapeNetCore 共有两个版本,两者在类别数量与文件组织方式上存在差异(以下信息以当前仓库源码中的文档字符串为准,见 pytorch3d/datasets/shapenet/shapenet_core.py 中ShapeNetCore.__init__的说明):
- v1:包含 57 个类别,每个模型存放为
model.obj文件。注意 v1 中有两个类别——02858304(boat,船)与02992529(cellphone,手机)——分别是04530566(watercraft,水上交通工具)与04401088(telephone,电话)的下位词(hyponym),如需合并可按需自行处理。 - v2:包含 55 个类别,与 v1 相比缺少
02858304(boat)与02834778(bicycle,自行车)两个类别,模型存放为models/model_normalized.obj文件。
在源码中,两个版本的模型文件路径通过self.model_dir区分:"model.obj" if version == 1 else "models/model_normalized.obj"。类别编号与标签的映射表分别存放在 pytorch3d/datasets/shapenet/shapenet_synset_dict_v1.json 与 pytorch3d/datasets/shapenet/shapenet_synset_dict_v2.json 中,加载时根据version参数自动选择对应字典文件。
ShapeNetCore 类与构造参数
ShapeNetCore继承自torch.utils.data.Dataset,构造时接收本地数据集路径,并遍历目录加载其中所有模型。其构造函数签名与各参数含义如下(详见 pytorch3d/datasets/shapenet/shapenet_core.py):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
data_dir | str | 必填 | ShapeNetCore 数据在本地的存放路径 |
synsets | List[str] | None | 要加载的类别列表,可用 synset 编号(如"04330267")或标签(如"guitar")表示,两者可混用;不指定时加载data_dir下所有官方映射中的类别 |
version | int | 1 | ShapeNetCore 版本,仅允许1或2,传入其他值会抛出ValueError("Version number must be either 1 or 2.") |
load_textures | bool | True | 是否加载模型纹理;为True时纹理以TexturesAtlas类型返回(每个面一张纹理图) |
texture_resolution | int | 4 | 每个面创建的纹理图分辨率,将生成(texture_resolution, texture_resolution, 3)的纹理图 |
目录结构的约定为:data_dir / <synset_id> / <model_id> / model.obj(v1),即每个模型的model_id是其所在子目录的名称。构造时会校验传入的类别是否存在(不存在则发出warnings.warn),并建立synset_start_idxs(各类别起始索引)与synset_num_models(各类别模型数量)映射,供后续按类别采样使用。
一个典型用法:
from pytorch3d.datasets import ShapeNetCore # 加载整个 ShapeNetCore v1 数据集 shapenet_dataset = ShapeNetCore(data_dir="/path/to/ShapeNetCore.v1", version=1) # 只加载部分类别(混用编号与标签) shapenet_subset = ShapeNetCore( data_dir="/path/to/ShapeNetCore.v1", synsets=["04330267", "guitar", "02801938", "birdhouse", "03991062", "tower"], version=1, ) # 不加载纹理 shapenet_no_texture = ShapeNetCore( data_dir="/path/to/ShapeNetCore.v1", load_textures=False, version=1 )getitem返回的数据结构
通过索引访问数据集时,ShapeNetCore.__getitem__返回一个字典(见 pytorch3d/datasets/shapenet/shapenet_core.py),包含以下键:
verts:FloatTensor,形状(V, 3),网格顶点坐标;faces:LongTensor,形状(F, 3),三角面片的顶点索引;synset_id:str,类别编号;model_id:str,模型编号;label:str,类别标签(由 synset 字典反查得到);textures:load_textures=True时为TexturesAtlas纹理((F, texture_resolution, texture_resolution, 3)),否则为None。
底层的网格加载由基类ShapeNetBase._load_mesh完成(见 pytorch3d/datasets/shapenet_base.py):它调用pytorch3d.io.load_obj,并传入create_texture_atlas、load_textures、texture_atlas_size三个参数;若某个模型没有纹理,会自动生成一张白色纹理图(全 1 张量),保证后续渲染不因缺纹理而失败。
通过 collate_batched_meshes 构造批处理
ShapeNetCore 加载的模型可以直接交给torch.utils.data.DataLoader使用,但需要配合 PyTorch3D 定制的collate_fn:collate_batched_meshes,它位于 pytorch3d/datasets/utils.py(并从 pytorch3d/datasets/init.py 导出)。
该函数的作用是:将一批__getitem__返回的字典列表合并为一个字典,其中每个键对应一个列表;更重要的是,当批数据同时包含verts与faces时,会用它们构造一个批量的Meshes对象(纹理若存在则以TexturesAtlas传入),作为collated_dict["mesh"]返回。Meshes是 PyTorch3D 的核心网格表示(定义于 pytorch3d/structures/meshes.py),支持顶点/面片补齐(padding)、按批次广播,并可直接用于 PyTorch3D 的各种算子与渲染管线。
from torch.utils.data import DataLoader from pytorch3d.datasets import ShapeNetCore, collate_batched_meshes shapenet_dataset = ShapeNetCore(data_dir="/path/to/ShapeNetCore.v1") shapenet_core_loader = DataLoader( shapenet_dataset, batch_size=12, collate_fn=collate_batched_meshes ) batch = next(iter(shapenet_core_loader)) # batch["mesh"] 是一个 Meshes 对象,可直接参与渲染或几何运算 print(batch["mesh"].verts_padded().shape) # (12, V_max, 3) print(batch["mesh"].faces_padded().shape) # (12, F_max, 3)在 tests/test_shapenet_core.py 的test_collate_models中,可以找到对collate_batched_meshes形状、类型以及 DataLoader 输出批次结构的完整验证。
内置 render 函数:按需渲染模型
ShapeNetCore(通过基类ShapeNetBase.render,见 pytorch3d/datasets/shapenet_base.py)还提供了一个定制化的render方法,可以用 PyTorch3D 的可微渲染器渲染指定模型,支持四种选取方式:
- 按
model_ids(List[str]):渲染这些模型编号对应的模型; - 按
categories+sample_nums:从每个给定类别中随机采样sample_nums指定的模型数量进行渲染(sample_nums可与类别一一对应,也可只给一个整数自动广播); - 按
idxs(List[int]):渲染给定索引的模型(会校验索引是否越界); - 什么都不指定:随机采样
sample_nums中第一个数(默认为 1)个模型渲染。
render的完整签名还包括:
shader_type:着色器类型,可选HardPhongShader(默认)、SoftPhongShader、HardGouraudShader、SoftGouraudShader、HardFlatShader、SoftSilhouetteShader等;device:张量所在设备(str 或torch.device);**kwargs:接受渲染器支持的所有参数,如cameras、raster_settings、lights等。
渲染内部流程为:先通过_handle_render_inputs将选取方式转换为模型索引列表(见 pytorch3d/datasets/shapenet_base.py),再调用__getitem__加载网格并用collate_batched_meshes拼装成批量Meshes;若网格没有纹理则补一个全 1 的TexturesVertex;默认相机为FoVPerspectiveCameras;最后构建MeshRenderer(MeshRasterizer+ 指定shader_type的着色器)输出形状为(N, H, W, 3)的图像批次。若传入多个相机,网格会被extend广播,使每个模型在多个视角下各渲染一次;当相机数量与网格数量不匹配时,会抛出ValueError("Mismatch between batch dims of cameras and meshes.")。
类别采样底层使用torch.multinomial实现(见_sample_idxs_from_category),当采样数大于类别内模型数时会放回采样并发出警告。测试 tests/test_shapenet_core.py 中的test_render_shapenet_core展示了三种典型调用:
import torch from pytorch3d.datasets import ShapeNetCore from pytorch3d.renderer import FoVPerspectiveCameras, PointLights, RasterizationSettings, look_at_view_transform device = torch.device("cuda:0") torch.manual_seed(39) piano_dataset = ShapeNetCore("/path/to/ShapeNetCore.v1", synsets=["piano"]) R, T = look_at_view_transform(1.0, 1.0, 90) cameras = FoVPerspectiveCameras(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], diffuse_color=((0, 0, 0),), specular_color=((0, 0, 0),), device=device, ) # 按索引渲染 pianos = piano_dataset.render( idxs=list(range(3)), device=device, cameras=cameras, raster_settings=raster_settings, lights=lights, ) # shape (3, H, W, 3) # 按 model_ids 渲染 pianos_by_ids = piano_dataset.render( model_ids=["13394ca47c89f91525a3aaf903a41c90", "14755c2ee8e693aba508f621166382b0", "156c4207af6d2c8f1fdc97905708b8ea"], device=device, cameras=cameras, raster_settings=raster_settings, lights=lights, ) # 按类别随机采样渲染 mixed = piano_dataset.render( categories=["faucet", "chair"], sample_nums=[2, 1], device=device, cameras=cameras, raster_settings=raster_settings, lights=lights, )R2N2 数据加载器
数据集背景
R2N2(3D-R2N2,源自斯坦福大学)数据集包含 13 个类别,是 ShapeNetCore v1 的子集。与 ShapeNetCore 相比,R2N2 还额外提供:
- 每个物体的24 张不同视角渲染图(由 Blender 渲染);
- 每个物体的体素化(voxelized)模型。
绝大多数模型的 24 个视角都落在同一个 train/val/test 划分中,但有 8 个模型的视角被拆分到了 train 与 test 两个划分里。R2N2 数据可从其官方页面下载,官方同时提供划分文件(splits file)。
R2N2 类与构造参数
R2N2同样继承自torch.utils.data.Dataset,其构造函数(见 pytorch3d/datasets/r2n2/r2n2.py)比ShapeNetCore需要更多路径参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
split | str | 必填 | 数据划分,必须是"train"、"val"、"test"之一,否则抛出ValueError |
shapenet_dir | str | 必填 | ShapeNetCore v1 数据路径 |
r2n2_dir | str | 必填 | R2N2 数据集路径 |
splits_file | str | 必填 | 包含 train/val/test 划分的 JSON 文件路径 |
return_all_views | bool | True | 是否加载划分内的所有视角;设为False时每个模型随机选取一个视角 |
return_voxels | bool | False | 是否返回体素张量(形状(D, D, D)) |
views_rel_path | str | "ShapeNetRendering" | R2N2 渲染图在r2n2_dir下的相对路径;若该目录缺失,渲染图会被跳过(并发出警告) |
voxels_rel_path | str | "ShapeNetVoxels" | R2N2 体素数据在r2n2_dir下的相对路径;若缺失,体素会被跳过 |
load_textures | bool | True | 是否加载网格纹理 |
texture_resolution | int | 4 | 每面纹理图分辨率 |
构造时会读取splits_file中对应split的划分字典,逐类别、逐模型检查其在shapenet_dir中是否存在,并记录每个模型可用的视角列表(views_per_model_list)。初始化完成后,还会在终端以表格形式打印每个类别的实例数量汇总(通过tabulate)。
典型用法:
from pytorch3d.datasets import R2N2 r2n2_dataset = R2N2( split="train", shapenet_dir="/path/to/ShapeNetCore.v1", r2n2_dir="/path/to/R2N2", splits_file="/path/to/splits.json", return_voxels=True, )getitem返回的数据结构
R2N2.__getitem__(model_idx, view_idxs=None)返回的字典在ShapeNetCore的基础上(verts、faces、synset_id、model_id、label、textures)额外包含(见 pytorch3d/datasets/r2n2/r2n2.py):
images:FloatTensor,形状(V, H, W, C),其中V为返回的视角数量,为 R2N2 提供的原始渲染图(像素值归一化到[0, 1]);R:FloatTensor,形状(V, 3, 3),旋转矩阵;T:FloatTensor,形状(V, 3),平移向量;K:FloatTensor,形状(V, 4, 4),内参矩阵(来自 Blender,略作调整以适配 PyTorch3D 世界坐标系,源码中写死为固定张量);voxels:FloatTensor,形状(V, D, D, D),体素张量(仅当return_voxels=True且体素文件存在时返回)。
view_idxs参数允许按需只取部分视角:索引必须是该模型在当前 split 中可用的视角编号(范围通常在 0 到 23 之间);若传入的索引无效,则忽略并返回全部已加载视角(同时发出警告)。第二参数既可以整体传给__getitem__,也可以作为元组(model_idx, view_idxs)传入。
相机标定来源于每个模型rendering/rendering_metadata.txt中的azim, elev, yaw, dist_ratio, fov五个参数:距离由dist_ratio * MAX_CAMERA_DISTANCE(MAX_CAMERA_DISTANCE = 1.75,取自 R2N2 官方常量)计算,外参矩阵由compute_extrinsic_matrix(见 pytorch3d/datasets/r2n2/utils.py)根据方位角、仰角、距离构造,再经_compute_camera_calibration转换到 PyTorch3D 世界坐标系得到R、T。
体素读取链路为:read_binvox_coords解析.binvox文件(pytorch3d/datasets/r2n2/utils.py)得到非零体素坐标,align_bbox将体素包围盒对齐到网格顶点坐标系,最后voxelize结合 Blender 内参与外参矩阵投影,输出128 × 128 × 128(VOXEL_SIZE = 128)的体素张量(pytorch3d/datasets/r2n2/utils.py)。tests/test_r2n2.py 的test_load_R2N2对上述各字段的形状进行了逐项断言,例如默认返回 24 个视角、图像形状为(24, 137, 137, 3)、体素形状为(24, 128, 128, 128)。
通过 collate_batched_R2N2 构造批处理
与 ShapeNetCore 对应,R2N2 的定制collate_fn是collate_batched_R2N2(位于 pytorch3d/datasets/r2n2/utils.py)。它在collate_batched_meshes基础上继续处理:
images:若各模型视角数相同V,堆叠为(N, V, H, W, 3),否则退化为列表;R、T、K:若视角数一致,分别堆叠为(N, V, 3, 3)、(N, V, 3)、(N, V, 4, 4);voxels:堆叠为(N, V, S, S, S)(S为体素尺寸),否则退化为列表。
from torch.utils.data import DataLoader from pytorch3d.datasets import R2N2, collate_batched_R2N2 r2n2_loader = DataLoader( r2n2_dataset, batch_size=12, collate_fn=collate_batched_R2N2 ) batch = next(iter(r2n2_loader)) # batch["mesh"] -> Meshes # batch["images"] -> (12, V, H, W, 3) # batch["R"] -> (12, V, 3, 3) # batch["T"] -> (12, V, 3) # batch["K"] -> (12, V, 4, 4) # batch["voxels"] -> (12, V, 128, 128, 128)tests/test_r2n2.py 的test_collate_models验证了上述所有输出在 DataLoader 中的形状与类型。
render 函数与 BlenderCamera
R2N2.render(见 pytorch3d/datasets/r2n2/r2n2.py)继承并扩展了ShapeNetBase.render的能力。除model_ids、categories、sample_nums、idxs、shader_type、device、**kwargs外,它新增了view_idxs参数:当不显式传入cameras时,默认使用BlenderCamera——一种专门为 R2N2 标定设计的相机类(定义于 pytorch3d/datasets/r2n2/utils.py),其投影变换直接由K内参矩阵构成——从而以与 R2N2 原始渲染完全一致的方向渲染指定视角。这在对比重建结果与真实渲染图时非常有用。
import torch from pytorch3d.datasets import R2N2 from pytorch3d.renderer import PointLights, RasterizationSettings device = torch.device("cuda:0") torch.manual_seed(39) r2n2_dataset = R2N2("train", SHAPENET_PATH, R2N2_PATH, SPLITS_PATH) raster_settings = RasterizationSettings(image_size=512) lights = PointLights( location=torch.tensor([0.0, 1.0, -2.0], device=device)[None], diffuse_color=((0, 0, 0),), specular_color=((0, 0, 0),), device=device, ) # 使用 R2N2 自身标定,以指定视角渲染 r2n2_batch = r2n2_dataset.render( idxs=torch.randint(1000, (2,)).tolist(), view_idxs=torch.randint(24, (2,)).tolist(), device=device, raster_settings=raster_settings, lights=lights, ) # shape (4, H, W, 3),2 个模型 × 2 个视角render_cubified_voxels(pytorch3d/datasets/r2n2/utils.py)是配套的另一个实用函数:先用 PyTorch3D 的cubify算子(阈值为CUBIFY_THRESH = 0.2)把体素张量转换为网格,再以BlenderCamera渲染,方便直接可视化体素重建结果(tests/test_r2n2.py 的test_render_voxels有对应验证)。
数据加载的最佳实践与注意事项
综合两份数据加载器的实现与测试(tests/test_shapenet_core.py、tests/test_r2n2.py),在使用时有以下几点值得注意:
- 路径完整性:
ShapeNetCore只要求本地存在网格文件即可加载;R2N2则依赖shapenet_dir(网格)、r2n2_dir(渲染图与体素)与splits_file三者的对齐。若views_rel_path或voxels_rel_path对应目录缺失,加载器不会报错,而是自动跳过相应数据并发出警告,因此训练前应检查r2n2_dataset.return_images、return_voxels标志。 - 版本号校验:
ShapeNetCore(version=3)会直接抛出ValueError,版本只能是 1 或 2(tests/test_shapenet_core.py 的test_load_shapenet_core中对此有断言)。 - 渲染参数校验:
render对无效model_id抛ValueError,对越界idxs抛IndexError;相机批量维度与网格批量维度不一致时抛ValueError,测试test_catch_render_arg_errors(R2N2 版本见 tests/test_r2n2.py)覆盖了这些分支。 - 纹理处理:
load_textures=False时__getitem__返回的textures为None(tests/test_shapenet_core.py 的test_load_textures_false验证了这一点),而render内部会对无纹理网格自动补全白色纹理,保证渲染路径始终可用。 - Meshes 批处理:务必使用对应的
collate_fn(collate_batched_meshes/collate_batched_R2N2)而非 PyTorch 默认的collate,否则无法获得可直接用于 PyTorch3D 算子与渲染器的Meshes对象。
小结
PyTorch3D 的ShapeNetCore与R2N2数据加载器把"下载数据集 → 解析 OBJ / 渲染图 / 体素 / 相机标定 → 构造Meshes批处理 → 可微渲染"整条链路封装为标准的torch.utils.data.Dataset接口。ShapeNetBase提供公共的纹理加载、索引解析与渲染骨架,两个子类分别针对纯网格数据(ShapeNetCore)与多视图 + 体素数据(R2N2)做了专门扩展,其中BlenderCamera与视角级标定使得研究者可以精确复现 R2N2 原始渲染视角,为单视图三维重建、视图合成、体素重建等任务提供了开箱即用的数据基础。更多文件读写能力可进一步阅读 docs/notes/io.md 与 docs/notes/meshes_io.md。
- 人工智能
- 深度学习
- 计算机视觉
- 图形学
【免费下载链接】pytorch3d
PyTorch3D is FAIR's library of reusable components for deep learning with 3D data
相关推荐
PyTorch3D数据集全攻略:ShapeNet与CO3D数据加载与预处理
PyTorch3D数据集全攻略:ShapeNet与CO3D数据加载与预处理 PyTorch3D是Facebook AI Research开发的3D深度学习库,提
人工智能深度学习计算机视觉图形学fastai数据模块深度解析:DataBlock与数据加载器
fastai数据模块深度解析:DataBlock与数据加载器 本文深入探讨了fastai框架中DataBlock API的架构设计与实现原理。DataBlock
人工智能深度学习LSUN数据集深度解析:MindSpore加载与应用全指南
LSUN数据集深度解析:MindSpore加载与应用全指南 在计算机视觉领域,大规模数据集的构建与高效利用一直是推动算法发展的核心动力。LSUN(Large s
深度学习计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考