nerfstudio ns-export 完全指南:从 NeRF 模型导出点云、网格与相机位姿
【免费下载链接】nerfstudioA collaboration friendly studio for NeRFs项目地址: https://gitcode.com/GitHub_Trending/ne/nerfstudio
ns-export是 nerfstudio 项目统一的模型导出命令行工具,负责把训练完成的 NeRF 模型转换为点云、三角网格、纹理化 OBJ、相机位姿与 3D Gaussian Splatting 点云等下游可直接使用的格式。本文以 ns_export 参考文档 为主线骨架,结合 nerfstudio/scripts/exporter.py 及其底层导出工具源码,系统讲解全部 6 个子命令的参数语义、执行流程与适用场景。读完本文,你将能够根据目标(三维重建、渲染、可视化或导入游戏引擎)选择正确的导出方式,并掌握每个关键参数的调优依据。
说明:
docs/reference/cli/ns_export.md是一份通过 Sphinxargparse指令自动生成的命令行参考页(其参数、默认值与帮助文本均来自nerfstudio.scripts.exporter模块的get_parser_fn),因此本文的参数语义与默认值全部以 nerfstudio/scripts/exporter.py 源码为准。
一、ns-export 是什么:CLI 结构与入口
ns-export在 pyproject.toml 中注册为控制台脚本,指向nerfstudio.scripts.exporter:entrypoint。入口函数通过tyro.cli(Commands).main()解析命令行,其中Commands是 6 个子命令的联合类型(见 exporter.py):
| 子命令 | 对应类 | 主要产物 |
|---|---|---|
ns-export pointcloud | ExportPointCloud | point_cloud.ply点云 |
ns-export tsdf | ExportTSDFMesh | tsdf_mesh.ply(+ 纹理化mesh.obj) |
ns-export poisson | ExportPoissonMesh | poisson_mesh.ply(+ 纹理化mesh.obj) |
ns-export marching-cubes | ExportMarchingCubesMesh | sdf_marching_cubes_mesh.ply(+ 纹理化mesh.obj) |
ns-export cameras | ExportCameraPoses | transforms_train.json/transforms_eval.json |
ns-export gaussian-splat | ExportGaussianSplat | splat.ply(3DGS 格式) |
所有子命令都继承自基类Exporter,包含两个公共参数:
load_config:训练时生成的配置 YAML 文件路径(ns-train会在输出目录中生成,例如outputs/<experiment>/<config-name>/config.yml)。output_dir:导出结果输出目录,不存在时会自动创建。
每个子命令的main()首先调用eval_setup(self.load_config)恢复训练时的 Pipeline(模型权重、数据管理器、优化器状态等),随后按各自算法完成导出。查看任意子命令的完整参数列表可运行:
ns-export pointcloud --help ns-export --help # 查看全部子命令二、导出点云:ns-export pointcloud
点云导出是其他网格导出(如 Poisson)的基础步骤,命令如下:
ns-export pointcloud --load-config CONFIG.yml --output-dir OUTPUT_DIR2.1 参数表
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--num-points | int | 1000000 | 目标生成点数;启用离群点剔除后实际点数可能更少 |
--remove-outliers | bool | True | 是否用统计方法剔除离群点 |
--reorient-normals | bool | True | 是否依据视线方向重定向法线(保证法线朝外) |
--normal-method | open3d/model_output | model_output | 法线来源:open3d 几何估计,或模型直接输出的法线 |
--normal-output-name | str | normals | 模型输出中法线字段名(normal_method=model_output时生效) |
--depth-output-name | str | depth | 模型输出中深度字段名 |
--rgb-output-name | str | rgb | 模型输出中 RGB 字段名 |
--obb-center/--obb-rotation/--obb-scale | float 三元组 | None | 有向包围盒(OBB)的中心、旋转(RPY 欧拉角,弧度)、三轴缩放;三者同时给出时按包围盒裁剪点云 |
--num-rays-per-batch | int | 32768 | 每批评估的光线数,内存不足时调小 |
--std-ratio | float | 10.0 | 离群点剔除阈值:以点云平均距离的标准差倍数计 |
--save-world-frame | bool | False | 为True时输出原始数据集坐标系下的点云,否则使用 NeRF 模型期望的缩放重定向坐标系 |
2.2 源码执行流程
ExportPointCloud.main()的关键调用链(exporter.py):
eval_setup恢复 Pipeline,随后validate_pipeline检查normal_method="model_output"时模型输出中确实存在法线字段(不存在会给出警告并退出,提示改用--normal-method open3d或训练带法线预测的模型,见 validate_pipeline)。- 临时调大
train_pixel_sampler.num_rays_per_batch以加速采样。 - 若三个
obb_*参数同时给出,构造OrientedBox作为裁剪区域。 - 调用
generate_point_cloud(exporter_utils.py):- 循环调用
pipeline.datamanager.next_train(0)取光线束,前向得到outputs; - 用
model.get_rgba_image合成 RGBA,按透明度 > 0.5过滤无效点,用origins + directions * depth反投影出三维点; - 可选 OBB 裁剪;
remove_outliers=True时执行remove_statistical_outlier(nb_neighbors=20, std_ratio=std_ratio); - 法线处理:
estimate_normals=True(即normal_method=open3d)时用 open3d 估计;否则直接取模型输出并把[0,1]范围法线映射回[-1,1];随后按视线方向重定向使法线朝外。
- 循环调用
save_world_frame=True时调用数据解析器的transform_poses_to_original_space把点坐标逆变换回原始空间。- 颜色量化为 UInt8 后,用 open3d 的
o3d.t.io.write_point_cloud写入point_cloud.ply(二进制格式,节省空间)。
要点:model_output法线要求训练时模型已输出法线(例如 nerfacto 开启法线预测),否则请改用open3d;OBB 裁剪可用于只导出场景中的特定区域。
三、导出网格:三种算法选型
3.1 TSDF Fusion:ns-export tsdf
TSDF(截断符号距离函数)融合基于深度图累积截断有符号距离场,再用 marching cubes 提取等值面。对任意模型都适用,是最通用的网格导出方式:
ns-export tsdf --load-config CONFIG.yml --output-dir OUTPUT_DIR参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--downscale-factor | int | 2 | 渲染深度图时相对训练分辨率的降采样倍数 |
--depth-output-name/--rgb-output-name | str | depth/rgb | 模型深度、颜色输出字段名 |
--resolution | int 或[x,y,z] | [128,128,128] | TSDF 体素网格分辨率,可整体指定或逐轴指定 |
--batch-size | int | 10 | 每批融合的深度图数量 |
--use-bounding-box | bool | True | 是否用包围盒限定 TSDF 体积 |
--bounding-box-min/--bounding-box-max | float 三元组 | (-1,-1,-1)/(1,1,1) | 包围盒范围(use-bounding-box=True时生效) |
--texture-method | tsdf/nerf | nerf | 网格纹理来源:tsdf用 TSDF 融合的颜色,nerf用 NeRF 重新渲染纹理 |
--px-per-uv-triangle | int | 4 | 每个 UV 三角形分配的像素数(自定义展开法) |
--unwrap-method | xatlas/custom | xatlas | UV 展开方式 |
--num-pixels-per-side | int | 2048 | xatlas 展开时纹理图每边像素数 |
--target-num-faces | int | 50000 | 纹理化前网格的目标面数(二次边折叠简化) |
--refine-mesh-using-initial-aabb-estimate | bool | False | 是否用首次网格的 AABB 细化 TSDF 体积 |
--refinement-epsilon | float | 1e-2 | 细化时 AABB/OBB 各方向外扩距离(米) |
源码流程(ExportTSDFMesh.main + tsdf_utils.py):
- 用
render_trajectory按1/downscale_factor缩放分辨率渲染全部训练视角的 RGB 与深度图(disable_distortion=True关闭畸变)。 - 根据
use_bounding_box选择用户包围盒或数据集scene_box.aabb,TSDF.from_aabb初始化体素网格(值初始化为-1,权重为 0)。 - 分
batch_size批调用tsdf.integrate_tsdf:把每个体素投影到相机坐标系与图像平面,grid_sample采样深度与颜色,计算截断距离clamp(dist/truncation, -1, 1)并做带权融合(旧值权重与新权重 1.0 加权平均,权重上限钳制为 1)。 tsdf.get_mesh()在 CPU 上用 scikit-image 的measure.marching_cubes提取零等值面,把体素坐标映射回世界空间。refine_mesh_using_initial_aabb_estimate=True时,先按首轮网格的 AABB 外扩refinement_epsilon重建更紧凑的 TSDF 再融合一遍,可显著提升表面精度。- 通过 pymeshlab 导出
tsdf_mesh.ply;texture_method="nerf"时再对网格做 UV 展开并用 NeRF 渲染纹理,输出mesh.obj+material_0.mtl+material_0.png。
3.2 Poisson 表面重建:ns-export poisson
Poisson 表面重建质量最高,但要求模型能计算或预测法线(例如 nerfacto)。官方推荐两步走(见 快速上手文档):
# 1. 训练带法线预测的 nerfacto ns-train nerfacto --pipeline.model.predict-normals True # 2. 用 Poisson 算法导出网格 ns-export poisson --load-config CONFIG.yml --output-dir OUTPUT_DIR参数表:除与pointcloud相同的点云参数(num-points、remove-outliers、reorient-normals、normal-method、normal-output-name、obb-*、num-rays-per-batch、std-ratio、depth/rgb-output-name)外,还包括:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--save-point-cloud | bool | False | 是否同时保存中间点云point_cloud.ply |
--texture-method | point_cloud/nerf | nerf | point_cloud直接保存重建网格,nerf用 NeRF 纹理化 |
--px-per-uv-triangle | int | 4 | 自定义 UV 展开每三角形像素数 |
--unwrap-method | xatlas/custom | xatlas | UV 展开方式 |
--num-pixels-per-side | int | 2048 | 纹理图边长 |
--target-num-faces | int | 50000 | 纹理化前目标面数 |
源码流程(ExportPoissonMesh.main):先生成点云(同pointcloud),再用 open3d 的create_from_point_cloud_poisson(pcd, depth=9)重建,随后按体密度低于 10% 分位数剔除低置信顶点,保存poisson_mesh.ply。texture_method="nerf"时加载网格、简化到target-num-faces后交给texture_utils.export_textured_mesh完成 UV 展开与 NeRF 纹理渲染。
注意:若法线质量差或点云存在孔洞,重建表面会出现伪影,可尝试增大num-points、调整std-ratio或关闭离群点剔除。
3.3 Marching Cubes 直接提取:ns-export marching-cubes
该子命令针对SDF 类模型(如 neus 等带sdf_field的模型),直接在稠密网格上采样 SDF 并提取零水平集:
ns-export marching-cubes --load-config CONFIG.yml --output-dir OUTPUT_DIR参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--isosurface-threshold | float | 0.0 | 等值面阈值;SDF 方法中零水平集即表面 |
--resolution | int | 1024 | Marching cubes 分辨率,必须能被 512 整除(多分辨率算法要求) |
--simplify-mesh | bool | False | 是否简化网格(当前流程通过target-num-faces控制简化) |
--bounding-box-min/--bounding-box-max | float 三元组 | (-1,-1,-1)/(1,1,1) | 提取范围 |
--px-per-uv-triangle/--unwrap-method/--num-pixels-per-side/--target-num-faces | — | 4/xatlas/2048/50000 | 纹理化参数 |
源码流程(ExportMarchingCubesMesh.main):首先断言pipeline.model.config存在sdf_field属性,否则直接报错;随后调用 marching_cubes.py 的多分辨率实现:把包围盒划分为(resolution/512)³个粗网格块,对每个块用 4 层点金字塔(512→256→128→64,平均池化下采样)渐进式评估 SDF,只在与零交叉附近细化,最后用 scikit-image marching cubes 逐块提取并拼接、合并顶点,输出sdf_marching_cubes_mesh.ply,再纹理化输出 OBJ 系列文件。
要点:resolution必须是 512 的倍数(源码断言resolution % 512 == 0),且该算法依赖 CUDA(points.cuda()),需要 GPU 环境。
四、导出相机位姿:ns-export cameras
用于把训练集/评估集的相机位姿导出为与 Blender/NeRF 数据集约定一致的 JSON 文件:
ns-export cameras --load-config CONFIG.yml --output-dir OUTPUT_DIR产物:transforms_train.json与transforms_eval.json,每个 frame 结构为:
{ "file_path": "path/to/image.png", "transform": [4x4 camera-to-world 矩阵] }源码流程(ExportCameraPoses.main + collect_camera_poses):
- 要求 Pipeline 为
VanillaPipeline。 - 遍历训练/评估数据集的相机,逐帧写入
file_path与camera_to_worlds位姿矩阵。 - 若模型带有
camera_optimizer(训练时开启了相机位姿优化),训练帧会通过camera_optimizer.apply_to_camera应用优化后的位姿;评估帧始终导出原始位姿。 - 某一数据集为空时跳过对应文件并给出提示。
该输出可直接用于在新工具/管线中重建相同视角,或作为二次训练、数据转换的输入。
五、导出 3D Gaussian Splatting:ns-export gaussian-splat
把训练好的splatfacto模型导出为 Inria 3DGS 兼容的二进制 PLY:
ns-export gaussian-splat --load-config CONFIG.yml --output-dir OUTPUT_DIR参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--output-filename | str | splat.ply | 输出文件名 |
--obb-center/--obb-rotation/--obb-scale | float 三元组 | None | 同点云导出,用于裁剪高斯 |
--ply-color-mode | sh_coeffs/rgb | sh_coeffs | rgb导出红绿蓝字段;否则导出球谐系数字段 |
源码流程(ExportGaussianSplat.main):
eval_setup(..., test_mode="inference")恢复模型,断言为SplatfactoModel。- 在
torch.no_grad()下从模型张量读取每个高斯的means、opacities、scales、quats(四元数旋转)与颜色/球谐系数:ply_color_mode="rgb"时导出red/green/blue(若模型sh_degree > 0会提示忽略高阶球谐);sh_coeffs时导出f_dc_i(直流项)与f_rest_i(高阶项,transpose(1,2)后拍平以对齐 Inria 版本球谐顺序)。
- 可选 OBB 裁剪(
crop_obb.within)。 - 导出前过滤两类高斯:含 NaN/Inf 的点(逐字段统计并打印数量),以及不透明度低于
logit(1/255) ≈ -5.5373的高斯(低于该值在 CUDA 光栅化中被跳过,导出它们无意义)。 write_ply(exporter.py)写入二进制小端序PLY:头部带comment Generated by Nerstudio <version>、comment Vertical Axis: z,float 统一转 float32,颜色用 uchar。
测试佐证:tests/scripts/test_exporter.py 中test_export_gaussian_splat_write_ply验证了write_ply写出的 PLY 可被 open3d 读回且坐标、颜色一致;test_export_gaussian_splat_write_ply_mismatched_count验证顶点数与张量长度不一致时抛ValueError。该格式可直接被常见 3DGS 查看器加载。
六、配套功能:为已有网格重新纹理化
若你已在外部工具(如 Blender、MeshLab)中简化或平滑过网格,可以用独立脚本 nerfstudio/scripts/texture.py 以 NeRF 重新着色(该脚本同时是ns-export纹理化子流程的底层复用模块):
python nerfstudio/scripts/texture.py \ --load-config CONFIG.yml \ --input-mesh-filename FILENAME \ --output-dir OUTPUT_DIR支持 PyMeshLab 能读取的网格格式(如.ply)。TextureMesh参数与ns-export的纹理化参数一致:px-per-uv-triangle=4、unwrap-method=xatlas|custom、num-pixels-per-side=2048、target-num-faces=50000。
纹理化原理(texture_utils.export_textured_mesh):
- 用 xatlas(或自定义的逐三角形 UV 布局)展开网格,得到每个面片到纹理图 UV 的映射。
- 对纹理图每个像素,通过重心坐标插值得到三维位置(光线原点)与法线方向(光线方向),法线取反并归一化。
- 以网格边长均值估算光线长度
raylen = 2 * mean(edge_len),构造RayBundle沿法线方向对 NeRF 做一次批量渲染,得到纹理颜色。 - 输出
material_0.png(纹理图)、material_0.mtl(材质)、mesh.obj(几何 + UV + 法线 + 面索引)。
unwrap-method=xatlas时纹理图边长为num-pixels-per-side(默认 2048);custom时按px-per-uv-triangle计算。
七、依赖与实战建议
运行依赖(由 pyproject.toml 的 pip 依赖提供):
- xatlas-python:网格 UV 展开(xatlas 方法);
- pymeshlab:网格面数简化(二次边折叠)与网格文件读写;
- open3d:点云生成/离群点剔除/法线估计/Poisson 重建/PLY 读写;
- scikit-image:marching cubes 等值面提取。
实战选型建议:
- 只要几何/点云:
ns-export pointcloud,注意normal-method的选择与obb-*裁剪。 - 通用网格:
ns-export tsdf,对任意模型可用;追求更高精度时开启refine-mesh-using-initial-aabb-estimate。 - 最高质量网格:先用
ns-train nerfacto --pipeline.model.predict-normals True训练,再ns-export poisson。 - SDF 模型(如 neus):
ns-export marching-cubes,resolution取 512 的倍数。 - 位姿复用:
ns-export cameras;3DGS 场景:ns-export gaussian-splat导出可在标准查看器中加载的splat.ply。
内存调优:点云与 Poisson 导出遇到 OOM 时降低--num-rays-per-batch(默认 32768)与--num-points;TSDF 导出降低--resolution或调大--downscale-factor。
所有导出命令都要求先完成训练并保留config.yml(训练命令与流程参见 first_nerf 快速上手);TSDF 与 Poisson 的完整工作流示例见 导出几何体文档。
【免费下载链接】nerfstudioA collaboration friendly studio for NeRFs项目地址: https://gitcode.com/GitHub_Trending/ne/nerfstudio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考