ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

instant-ngp实战:Ubuntu20.04从数据集制作到NeRF模型训练与Mesh导出全流程

instant-ngp实战:Ubuntu20.04从数据集制作到NeRF模型训练与Mesh导出全流程 1. 项目概述与整体规划1.1 核心需求解析instant-ngpInstant Neural Graphics Primitives是NVIDIA开源的一套用于快速训练和渲染神经辐射场NeRF的框架。它的核心卖点就一个字快。传统NeRF训练一个场景动辄几小时甚至几天而instant-ngp基于cuda和cuda Core的小型MLP加上多分辨率哈希编码能在几十秒到几分钟内完成一个高质量场景的重建。我在Ubuntu20.04上完整走了一遍从编译、自建数据集、训练到导出mesh的流程整个过程踩了不少坑这次把全流程和遇到的问题一起整理出来。这篇内容适合谁看两类人。第一类是想用instant-ngp做三维重建的算法工程师和研究生你需要把自己的数据灌进去跑出结果第二类是准备做NeRF或三维视觉方向开发想先在本地完整跑通一条链路的人。文章目标很明确在Ubuntu20.04上从零把instant-ngp跑起来用自己的数据集训练出神经辐射场最后导出可用的mesh模型文件。1.2 技术栈选型分析先说选型。instant-ngp官方支持Windows和Linux。为什么选Ubuntu20.04而不选Windows实际体验下来Linux下的编译链更干净依赖处理更直接。Windows也能跑但遇到CUDA版本冲突时处理起来比Linux麻烦。官方推荐环境是Ubuntu 20.04/22.04我实测20.04完全没问题CUDA 11.x以上官方建议11.6显卡NVIDIA Turing架构以上RTX 20系及其后续因为依赖光流和光栅化特性但也有办法在老卡上编译CMake 3.21支持C17的GCC/G我的实际环境系统Ubuntu 20.04.6 LTS显卡RTX 3080显存10GBCUDA11.8cuDNN8.6.0GCC9.4.0CMake3.22.1这套组合用下来最稳机器配置低一点的话稍微有点吃力特别是导出mesh时对显存有一定要求。显存低于6GB的卡建议训练时降低分辨率、减小max_n_samples或者降低采样步长。整条链路可以拆成四步环境准备、数据集制作、训练、导出mesh。这四步环环相扣每步都有基本功要练接下来逐一拆解。2. 环境准备驱动、CUDA与依赖安装2.1 NVIDIA驱动与CUDA版本匹配策略环境准备是整个流程里最容易让人崩溃的一环。instant-ngp对CUDA的要求并不苛刻但版本不对会直接导致编译失败或者运行闪退。先装NVIDIA驱动。这里有个最大的误区直接用apt install nvidia-driver-xxx安装可能拿到的是系统仓库里自带的旧版本。Ubuntu 20.04默认仓库里的驱动版本偏老要是你的显卡是RTX 30系或更新的建议走官方PPA或者直接下载runfile安装。我推荐的安装方式sudo add-apt-repository ppa:graphics-drivers/ppa sudo apt update sudo apt install nvidia-driver-535 sudo reboot装完检查一下nvidia-smi如果能正常输出显卡信息和驱动版本说明驱动就位。注意驱动版本和CUDA版本没有强绑定关系但CUDA toolkit的版本要向下兼容驱动。你可以用nvidia-smi查看右上角的CUDA Version这个数字表示驱动支持的最高CUDA版本。比如显示CUDA Version: 12.2就意味着最高支持CUDA 12.2装11.8完全没问题。CUDA我推荐用11.8。为什么不用12.x因为instant-ngp的官方release在CUDA 12下需要自己适配一些编译参数而11.8有大量现成的编译经验第三方库的兼容性也更好。下载地址用NVIDIA官方archive选择runfile方式安装。wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run sudo sh cuda_11.8.0_520.61.05_linux.run安装时注意如果已经装好了驱动在安装界面里取消勾选Driver那一项只安装CUDA Toolkit。装完后配置环境变量echo export PATH/usr/local/cuda-11.8/bin:$PATH ~/.bashrc echo export LD_LIBRARY_PATH/usr/local/cuda-11.8/lib64:$LD_LIBRARY_PATH ~/.bashrc source ~/.bashrc验证nvcc -V2.2 基础依赖与编译链整理instant-ngp编译还需要以下依赖sudo apt install -y build-essential git cmake cmake-curses-gui sudo apt install -y libglfw3-dev libglm-dev libxxf86vm-dev libxcursor-dev其中libglfw3-dev用于窗口管理libglm-dev提供OpenGL数学库libxxf86vm-dev和libxcursor-dev是窗口系统扩展。这些缺任何一个CMake配置阶段都会报错。CMake版本必须在3.21以上。Ubuntu 20.04自带的是3.16不满足要求。升级方法sudo apt remove cmake wget https://github.com/Kitware/CMake/releases/download/v3.26.4/cmake-3.26.4-linux-x86_64.tar.gz tar -zxvf cmake-3.26.4-linux-x86_64.tar.gz sudo mv cmake-3.26.4-linux-x86_64 /opt/cmake sudo ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake再就是cuDNN。如果你只跑NeRF训练cuDNN不是必需的但如果你后续要跑一些基于CNN的预处理网络或者要编译完整版instant-ngp的某些依赖就建议提前装好。cuDNN需要NVIDIA官网注册后下载选择与CUDA 11.8匹配的版本。另外如果安装时提示libcudart.so找不到多半是CUDA的软链接没配上。检查/usr/local/cuda/lib64/libcudart.so.11.0是否存在然后创建libcudart.so软链接指向它。这个问题容易忽略但编译时很常见。2.3 instant-ngp源码获取与编译参数解读git clone --recursive https://github.com/nvlabs/instant-ngp.git cd instant-ngp--recursive参数别漏这个仓库有大量submodule包括tiny-cuda-nn、torch等第三方依赖。漏了这个参数后面CMake配置一定会报tiny-cuda-nn找不到。如果你下载时网络比较慢可以先用普通方式git clone然后单独拉子模块git submodule update --init --recursive编译cmake -B build -DCMAKE_BUILD_TYPERelWithDebInfo cmake --build build --config RelWithDebInfo -j 16这里有两个点要说明。-j 16表示用16线程并行编译可根据CPU核心数调整内存不足的情况下适当减少避免编译时内存爆掉。RelWithDebInfo编译类型保留调试信息和优化既保证性能又方便排查问题。如果想追求极致性能可以用Release但出错时排查难度会增加。编译过程大概需要10到20分钟取决于机器配置。如果程序卡在某个库的下载上多半是网络问题切换网络源后重试。编译完成后检查一下可执行文件ls build/如果看到testbed、img2col、nerf等可执行文件说明编译成功。3. 自建数据集的完整制作流程3.1 拍摄准备与相机参数选择数据集质量直接决定重建效果。instant-ngp虽然对输入数据的容忍度比传统NeRF高但采集环节不规范后面怎么调参数都白搭。拍摄用的是普通手机。手持拍摄也能用但要注意几个原则。重叠率要够。相邻两张图片之间的重叠区域建议在70%到80%之间。重叠太少特征点匹配困难重建出的相机位姿会飘。重叠率这个概念可以类比拼图每块拼图之间的图案必须够接近才能准确拼到一起。角度要有变化但不要太极端。围绕目标物体拍摄一圈机位高度可以适当变化让相机有俯仰角度的变化覆盖更多模型的顶部和底部细节。但不要突然从很近的距离切到很远的距离这样会让位姿估计算法犯迷糊。光照要均匀稳定。最好在散射光环境下拍摄比如阴天或者用灯罩柔化过的室内灯光。强阴影和强高光区域在NeRF训练中很难拟合特别是反光强烈的物体会出现漂浮的伪影。背景不要太杂乱。instant-ngp默认会建模整个场景背景太乱会消耗大量的网络容量去拟合无用的信息导致目标物体细节不足。如果无法控制环境尽量用纯色背景布。具体到我的拍摄方案围着一个人偶模型在直径约1米的圆周上每隔5到10度拍一张同时相机在水平位置上下各抬高和降低约20度角一共采集了200多张图。手机固定光圈ISO自动关闭HDR和滤镜拍RAW格式后统一转成JPEG。3.2 图片预处理ffmpeg抽帧与缩放如果拍的是视频要先抽帧。用ffmpeg一条命令ffmpeg -i input_video.mp4 -vf fps30 frames/%04d.jpg这里fps30表示每秒抽30帧。注意一个问题视频帧率太高会导致相邻帧之间画面差异太小位姿估计时会出现退化。一般一秒钟抽15到30帧够用拍摄视频时长控制在30秒到1分钟绕物体一圈就能得到几百帧数据。抽出来的原始帧往往尺寸太大。手机拍出来的图可能是4032x3024直接喂给instant-ngp有几个问题显存占用过高、训练速度变慢、高频细节容易过拟合出现噪点。官方推荐的分辨率是1600x1200以下。我统一缩放到1280x960ffmpeg -i frames/%04d.jpg -vf scale1280:960 frames_processed/%04d.jpg同时用-q:v 2控制JPEG质量尽量保留细节ffmpeg -i frames/%04d.jpg -vf scale1280:960 -q:v 2 frames_processed/%04d.jpg3.3 通过COLMAP估算相机位姿instant-ngp支持两种方式获得相机位姿自己提供匹配好的相机参数或者用COLMAP自动估算。后者是绝大多数人的选择但COLMAP的安装和调用有坑。先装COLMAPsudo apt install colmapUbuntu 20.04自带的COLMAP是3.6版本对这个流程够用。如果编译出错或者需要SIFT GPU加速可以自己从源码编译但那是一个比较大的工程建议先试系统自带的。把处理好的图片放到一个独立目录结构如下data/ └── custom_scene/ ├── images/ │ ├── 00001.jpg │ ├── 00002.jpg │ └── ... └── ...然后运行instant-ngp的自动位姿估计脚本cd instant-ngp python scripts/colmap2nerf.py --colmap_matcher sequential --images data/custom_scene/images --run_colmap --aabb_scale 4 --out_path data/custom_scene/transforms.json参数说明--colmap_matcher sequential按图像顺序匹配特征点适合视频抽帧得到的图像序列速度快且不容易出错--run_colmap自动调用COLMAP进行计算--aabb_scale 4设置场景包围盒大小。这个参数影响场景归一化的尺度数值越大场景范围越大。对单物体重建取2到4比较合适如果拍的是大空间取8到16脚本会依次执行特征提取、特征匹配、稀疏重建、相机位姿估计最后生成transforms.json。跑完后检查两个东西。第一transforms.json里frames的数量是否和输入图片数量一致第二COLMAP的输出是否成功日志里有没有出现Failed to register image的红色警告。如果有大量帧注册失败说明图片质量或重叠率有问题需要补充拍摄。另外推荐直接用--colmap_matcher exhaustive来做全局匹配准确率更高但速度慢很多。对几十到几百张图的场景其实也可以接受。3.4 数据集格式验证与常见问题生成的transforms.json结构大致长这样{ camera_angle_x: 0.691111, aabb_scale: 4, frames: [ { file_path: images/00001.jpg, transform_matrix: [...], }, ... ] }几个关键要点file_path是相对路径相对transforms.json所在目录transform_matrix是4x4矩阵表示相机外参旋转加平移camera_angle_x是水平视角弧度由COLMAP自动估算如果打开json发现camera_angle_x是0或者特别小通常是图片尺寸和实际焦距估算出了问题回到COLMAP重新跑或者检查图片有没有损坏。还有个细节colmap2nerf.py默认会把图片路径写成绝对路径如果你移动了数据目录需要改脚本参数。加上--aabb_scale可以改变场景边界。我的建议是训练前先把transforms.json备份一份后面调参时会反复用到。4. 模型训练与参数调试4.1 启动训练的关键命令与参数解析数据集就绪后启动训练cd build ./testbed --scene ../data/custom_scene/transforms.json如果一切正常会弹出一个GUI窗口左侧是训练画面右侧是参数面板。第一次运行会自动进入训练状态几秒钟后就能看到粗糙的场景轮廓。GUI窗口里需要关注几个参数max_n_samples每条光线上的最大采样点数量。值越大重建细节越丰富但显存占用和耗时同步上升。默认值是1024对一般场景够用如果显存紧张可以降到512。ray marching step size控制光线步进长度。默认0.01步长过大容易漏掉薄片结构步长过小训练变慢。对单物体扫描保持默认即可。aabb_scale和生成数据集时对应。如果数据集生成时设置的是4这里也要匹配否则场景拉伸变形。训练过程中观察Loss曲线。刚开始Loss会快速下降从几百降到个位数然后缓慢收敛。一般迭代到几百步时画面已经能看出物体轮廓到几千步时细节逐渐完善。4.2 从GUI训练到命令行批处理的切换技巧GUI方式适合交互调试但如果你要跑批量实验或者想在服务器上无人值守训练就得用命令行参数方式。./testbed --scene ../data/custom_scene/transforms.json --mode nerf --save_snapshot ../output/custom_scene.ingp --screenshot_transforms ../data/custom_scene/transforms.json --screenshot_frames 10 --width 1280 --height 960 --save_mesh ../output/custom_scene_mesh.ply参数说明--mode nerf指定训练模式instant-ngp支持nerf、sdf、image等多种模式--save_snapshot保存训练快照后面可以加载继续训练或做导出--screenshot_transforms和--screenshot_frames渲染指定视角的截图用于快速验证--save_mesh训练结束后导出mesh注意--save_mesh和--save_snapshot可以同时用但mesh导出会在训练收敛后才执行。如果直接跑完不指定迭代次数默认会跑很久。要控制训练时间加--n_steps./testbed --scene ../data/custom_scene/transforms.json --mode nerf --n_steps 10000 --save_snapshot ../output/custom_scene.ingp --save_mesh ../output/custom_scene_mesh.ply--n_steps 10000表示最多训练10000步。对单物体扫描8000到15000步一般就够了。4.3 训练效果优化分辨率、采样数与密度阈值训练出的效果不满意时优先检查以下方向图片分辨率。训练分辨率由GUI里的render resolution控制或者命令行里通过--width和--height指定。分辨率太低细节看不清分辨率太高显存不够。一般1280x960已经能给到很好的结果。采样步长和数量。如果场景边缘有雾状伪影试试降低ray marching step size到0.005或者提高max_n_samples到2048。但这种调整会让训练时间成倍增加。密度阈值。导出mesh时会用到密度阈值用于过滤低密度的游离体素。阈值设太高模型会变得破碎缺肉设太低会有大量噪点附着在模型表面。一般从默认值开始微调看效果。看一个有代表性的例子。我第一次拿真实拍摄的数据训练时模型表面有明显的半透明漂浮物像一层雾气贴着物体表。查了官方issue发现这类问题多为光线步长太大。把ray marching step size从0.01改为0.005体重建的干净度提升非常明显。训练过程中随时可以保存快照。我个人习惯每5000步保存一个snapshot万一后面调参数调坏了还能回到中间状态重新来。4.4 显存不足时的降级方案RTX 3080 10GB跑默认参数没有压力但换成6GB显存的卡就需要注意了。显存不够第一反应是降低渲染分辨率比如--width 640 --height 480这是有效但最粗糙的方案。更好的办法是减少批大小。instant-ngp的GUI里没有直接的batch_size参数但可以通过修改源码实现实际更常用的方式是限制训练分辨率的同时把max_n_samples降到512。还有一个方法是开启--no_tcnn模式。这个参数会禁用tiny-cuda-nn加速改用原生的PyTorch实现训练速度会慢很多但显存占用大幅下降。这是备用方案。如果用的是老显卡GTX 10系等编译时加-DNGP_USE_OPTIXOFF关闭OptiX光线追踪因为OptiX在新框架下才完整支持。这会牺牲一部分渲染效率但能保证程序跑起来。5. Mesh导出从神经场到几何表面5.1 导出mesh的底层原理简述训练好的NeRF模型本质上是三维空间中每个点的密度和颜色函数。Mesh导出的任务就是从这个连续函数里提取出一个离散的三角形网格表面。最常用的方法是Marching Cubes移动立方体。算法思路把空间划分成一个个小立方体网格在每个立方体内部根据密度函数的等值面与立方体边界的交点构建三角形面片。立方体越小网格越细腻但计算量越大。instant-ngp中导出mesh的命令./testbed --scene ../data/custom_scene/transforms.json --mode nerf --load_snapshot ../output/custom_scene.ingp --save_mesh ../output/custom_scene_mesh.ply --marching_cubes_res 512参数说明--load_snapshot加载训练好的模型快照--save_mesh输出mesh文件路径支持.ply格式--marching_cubes_resMarching Cubes分辨率即包围盒在每维度上的体素数量。默认256可以调到512甚至1024数值越大mesh越精细但耗时和内存占用指数级上升5.2 Marching Cubes分辨率选择与精度权衡分辨率这个参数的选择本质上是在精度和资源之间做权衡。我用--marching_cubes_res 256导出的mesh整体轮廓正确但细节丢失明显比如人偶的头发丝完全没有体现出来。把分辨率调到512后头发丝和衣服纹理开始出现文件大小从几十MB涨到几百MB。1024分辨率下细节更丰富但导出过程耗时十分钟以上导出后的PLY文件巨大普通三维软件打开都卡。实际建议要看效果用256快速预览确认没问题后直接512导出。特殊情况需要极高质量再用1024同时确保磁盘空间和内存充足。5.3 导出结果的检查与降噪处理导出完成后用MeshLab打开。MeshLab是免费的网格处理软件可以从Ubuntu软件中心直接安装sudo apt install meshlab打开后重点检查三个方面表面是否闭合。用Filters Cleaning and Repairing Check for watertight manifold。如果非流形边太多模型打印或后续处理都会有麻烦。是否有孤立碎片。Marching Cubes会把一些噪声点也提取成小三角片。用Filters Cleaning and Repairing Remove isolated pieces (wrt diameter)清理。法线方向是否一致。如果法线混乱渲染时会看到面片颜色斑驳。用Filters Normals, Curvatures and Orientation Re-Orient all faces coherently修正。如果mesh表面有小洞用Filters Remeshing, Simplification and Reconstruction Close Holes修补。加工完的mesh如果要放到游戏引擎或3D打印可以考虑做一次简化和纹理映射不过那是另一个话题了。5.4 补充OBJ格式与纹理导出要点很多场景需要带纹理的mesh而不是只有几何形状的PLY。instant-ngp原生导出不带纹理的PLY但可以通过以下方式获得OBJ格式。一种方式是用--save_mesh ../output/custom_scene_mesh.obj后缀直接改成obj但实测这个方法不可靠导出的obj只有几何信息没有纹理坐标。更好的方案在instant-ngp的GUI界面中点击Render面板设置输出路径和格式为.obj或者使用建议方案./testbed --scene ../data/custom_scene/transforms.json --mode nerf --load_snapshot ../output/custom_scene.ingp --save_mesh ../output/custom_scene_mesh.obj --marching_cubes_res 512这条命令生成带纹理坐标的obj文件。不过说实话instant-ngp自带的mesh导出机制更偏向于几何重建对纹理贴图的生成效果一般。如果对纹理要求高考虑后续用其他的开源工具如NeRF2Mesh、suGaR等做二次处理。当前阶段先把几何导出来跑通纹理映射单独再调。6. 实操心得与常见问题速查6.1 我踩过的几个比较隐蔽的坑第一次复现时我在数据集生成阶段卡了很长时间。colmap2nerf.py运行过程中报错反复检查才发现是图片路径包含中文和空格COLMAP只能识别ASCII路径。把数据集整体挪到纯英文路径下重新跑就通了。还有一个更隐蔽的问题。用手机拍视频抽帧时如果视频带有比较强的滚动快门效应抽出的帧在对齐时会出现微小的位置偏差这个偏差在训练中会被放大表现为物体表面出现弧线形伪影。解决方法是拍摄时尽量避免快速移动手机或者用机械稳定器。关于CUDA版本我也踩过一次坑。一开始图省事装了CUDA 12.1结果tiny-cuda-nn编译时报了一堆奇怪的错误。后来重新用CUDA 11.8一条命令直接编译通过。所以入门阶段建议严格跟随官方推荐的CUDA版本别用太新的。6.2 训练和导出问题排查清单问题原因解决方案编译时找不到tiny-cuda-nnsubmodule未拉取执行git submodule update --init --recursiveCOLMAP特征匹配失败图片重叠率低或模糊增加重叠率重新拍摄或抽帧训练时画面全黑数据集aabb_scale不符确保数据集生成和训练参数一致训练loss不降图片数量太少或相机位姿估算失败补充图片数量检查transforms.json中位姿矩阵导出mesh为空或空洞多密度阈值过高或Marching Cubes分辨率低调低密度阈值提高--marching_cubes_res运行时CUDA out of memory显存不足降低分辨率减小max_n_samples关闭其他程序GUI窗口无法打开缺少OpenGL显示环境安装libglfw3-dev libglm-dev或使用虚拟显示器 xvfb6.3 后续扩展方向跑通后用同一个流程可以做很多事情。可以尝试用这个流程重建自己房间的全景模型只要场景范围设置得当。可以把导出的mesh导入Blender做后期处理或者用3D打印机实物化。可以把instant-ngp的SDF模式也跑一遍对比NeRF和SDF哪种重建效果更适合你的场景。最后分享一个小窍门。训练好的.ingp文件其实很小一般几十MB到几百MB保存好它随时可以通过--load_snapshot快速加载并导出不同参数的mesh版本不需要重新训练。我实际用下来加载快照再导出整个过程可以在几分钟内完成大大提高了调参效率。这种迭代方式建议每个准备深入挖掘这套流程的人提前养成习惯前期多存几个快照后面省下的时间非常可观。
返回列表