Lighthouse 光照体积预测:基于多尺度光照体积的空间一致照明算法实战指南
【免费下载链接】google-researchGoogle Research项目地址: https://gitcode.com/gh_mirrors/go/google-research
导读
Lighthouse(Srinivasan et al., CVPR 2020)是一套从窄基线立体 RGB 图像对出发、预测场景内任意 3D 位置入射光照的算法实现,位于本仓库的 lighthouse/ 目录。本文以该目录下的 README.md 为骨架,结合源码(mlv.py、nets.py、train.py、interiornet_test.py)深入讲解其核心思想、环境搭建、预训练模型推理与自训练流程,帮助你理解多尺度 RGBA 光照体积的预测与体渲染合成原理,并能在本地复现论文中的测试与训练管线。
一、算法核心思想:从立体图像对到光照体积
Lighthouse 解决的核心问题是:给定场景中某个 3D 位置,预测该位置的入射光照(incident illumination)。与常见的光照估计方法(如直接回归环境贴图)不同,它预测的是场景中任意位置的光照,从而实现空间一致(Spatially-Coherent)的照明效果。
从 README.md 的说明可以看到完整处理链路:
- 输入:一对窄基线(narrow-baseline)立体 RGB 图像;
- 中间表示:先由多平面图像(Multiplane Image, MPI)网络预测 RGBA 分层表示;
- 核心输出:预测一个多尺度 RGBA 光照体积(multiscale RGBA lighting volume);
- 渲染:通过标准的体渲染(volume rendering)从光照体积中计算空间变化的照明。
整体管线在 mlv.py 的MLV类中实现,包含四个关键阶段:
infer_mpi():从参考图 + 源图 + 位姿构造平面扫描体(Plane Sweep Volume, PSV),再经 3D 编解码网络预测 MPI;predict_lighting_vol():将 MPI 重采样到以目标位置为中心的多尺度立方体(cube)光照体积;cube_net_multires()(定义于 nets.py):用多分辨率 3D U-Net 细化各级光照立方体;render_envmap():把立方体体积重采样到球面坐标系,沿半径方向采样多层球壳,最终合成环境贴图(environment map)。
立体图像对 │ (窄基线 RGB 对) ▼ MPI 预测 (mpi_net 3D encoder-decoder) │ RGBA 分层表示 ▼ MPI → 多尺度光照立方体 (predict_lighting_vol) │ cube_res=64, scale_factors=[2,4,8,16] ▼ 多分辨率立方体细化 (cube_net_multires) │ 从粗到细、嵌套式细化 ▼ 球面重采样 + 体渲染 → 环境贴图 (render_envmap)二、环境安装与代码补全(重要前提)
2.1 依赖版本
仓库在 requirements.txt 中声明了运行依赖,整体是一个基于 TensorFlow 1.x 的工程:
| 依赖 | 版本要求 | 用途 |
|---|---|---|
| tensorflow | ~=1.11.0 | 核心深度学习框架(源码使用tensorflow.compat.v1) |
| numpy | >=1.15.1 | 数值计算与 .npz 数据读写 |
| matplotlib | >=2.2.3 | 输出环境贴图 PNG |
| absl-py | >=0.6.1 | 命令行 flag 解析(app/flags) |
| scipy | >=1.1.0 | 加载 VGG 预训练权重(.mat) |
2.2 必须手动补全的外部代码
README.md 的 Installation 一节明确说明:本工程依赖部分非 Apache 2 许可证的外部代码库,需要在下载源码后手动补齐,否则无法运行:
lighthouse/nets.py:文件末尾注明需从PhotographicImageSynthesis项目的demo_1024p.py复制第 10~48 行,以补全判别器(discriminator)所需的辅助函数;lighthouse/geometry/projector.py:文件末尾注明需从 LLFF 项目的mpi_math.py复制第 6~191 行,补全体渲染(homography warping、PSV 构造等)核心函数。当前文件仅提供了tfmm()等占位 stub 以抑制 lint 错误。
注意:由于补全代码来自非 Apache 2 许可的代码库,请自行评估许可合规性;补全遇到困难时可联系作者(README 中给出 barron@google.com 与 pratul@berkeley.edu)。
三、运行预训练模型:interiornet_test.py
3.1 需要准备的文件
运行测试前需要下载两类外部资源(下载链接见 README.md):
- 预训练模型权重:解压后放入 checkpoint 目录;
- 测试样例:以
.npz格式组织的 InteriorNet 测试集子集,每个 npz 文件包含一次测试所需的全部输入。
3.2 命令行用法
测试脚本为 interiornet_test.py,接受三个 flag(interiornet_test.py):
| Flag | 默认值 | 说明 |
|---|---|---|
--checkpoint_dir | "" | 存放预训练 checkpoint 的目录 |
--data_dir | "" | InteriorNet 测试数据集目录(含 .npz 文件) |
--output_dir | "" | 输出环境贴图 PNG 的目录 |
README 给出的示例命令(请按实际目录结构修改路径):
python -m lighthouse.interiornet_test \ --checkpoint_dir="lighthouse/model/" \ --data_dir="lighthouse/testset/" \ --output_dir="lighthouse/output/"3.3 测试脚本背后的完整推理链路
从源码看,interiornet_test.py 构建的推理图完整复现了"MPI → 光照体积 → 环境贴图"三步:
- 定义占位符(placeholder):参考图
ref_image、参考深度ref_depth、内参intrinsics、参考位姿ref_pose、源图像src_images、源位姿src_poses、环境位姿env_pose; - 用
pj.inv_depths(min_depth, max_depth, num_planes)在逆深度空间均匀采样 32 个 MPI 平面(projector.py)。测试时直接使用真实深度的 min/max 作为平面范围,README 提示:换用你自己的数据集时,应改为估计的深度范围; model.infer_mpi()预测 RGBA 分层(rgba_layers);model.predict_lighting_vol()生成多尺度光照立方体;nets.cube_net_multires()细化立方体;model.render_envmap()渲染环境贴图,并将结果以{:05d}.png命名逐张写入输出目录。
脚本按.npz后缀扫描--data_dir下的所有文件(interiornet_test.py),并从 checkpoint 目录恢复model.ckpt(interiornet_test.py),因此目录结构与文件名约定不可随意更改。
3.4 推理阶段默认模型参数
测试脚本与训练脚本共享同一套模型超参(定义于 interiornet_test.py 与 train.py):
| 参数 | 值 | 含义 |
|---|---|---|
batch_size | 1 | 当前实现仅支持 batch size = 1 |
height/width | 240 / 320 | 输入参考/源图像分辨率(px) |
env_height/env_width | 120 / 240 | 环境贴图分辨率(px) |
cube_res | 64 | 光照立方体每边体素数 |
theta_res/phi_res | 240 / 120 | 环境贴图水平/垂直采样分辨率 |
r_res | 128 | 环境贴图渲染时球壳(半径方向)采样数 |
scale_factors | [2, 4, 8, 16] | 多尺度立方体相对最粗级的降采样倍数 |
num_planes | 32 | MPI 平面数量 |
depth_clip | 20.0 | 最远深度(限制最粗立方体范围),需按数据集调整 |
四、训练自己的模型
4.1 训练入口与数据集
训练入口为 train.py,README 建议先阅读 data_loader.py 了解 InteriorNet 数据组织方式。模型使用 InteriorNet 数据集训练,数据加载器支持以下机制:
ViewSequence/ViewTrip:把一段相机轨迹抽象为视图序列,通过generate_trips()在帧间按min_gap~max_gap的偏移量生成四元组(triplet + 远处相机全景图),并用random_subsequence()做随机降采样(data_loader.py);prepare_training_set():依次过滤随机光照序列、小平移样本、过暗样本、倒退全景样本、过近场景样本,再进入shuffle(1000000)+batch(1)+prefetch管线(data_loader.py);format_inputs():把原始 480×640 图像缩放到 240×320,并相应缩放内参矩阵,同时输出参考图/目标图/源图、位姿、深度与环境图监督信号(data_loader.py)。
4.2 训练命令行参数
train.py 定义四个 flag:
| Flag | 默认值 | 说明 |
|---|---|---|
--vgg_model_file | "" | VGG19 预训练权重文件(imagenet-vgg-verydeep-19.mat)路径 |
--load_dir | "" | 用于断点续训的 checkpoint 目录(为空则从 experiment_dir 内恢复) |
--data_dir | "" | InteriorNet 数据集目录 |
--experiment_dir | "" | 保存 summaries 与 checkpoints 的实验目录 |
README 给出的训练示例命令:
python -m lighthouse.train \ --vgg_model_file="lighthouse/model/imagenet-vgg-verydeep-19.mat" \ --load_dir="" \ --data_dir="lighthouse/data/InteriorNet/" \ --experiment_dir=lighthouse/training/其中--vgg_model_file需要从 MatConvNet 预训练模型库下载imagenet-vgg-verydeep-19.mat(论文中用于基于 VGG 特征的感知损失)。若不需要感知损失,可从 mlv.py 的build_train_graph()中看到,渲染损失与环境贴图损失的权重均来自 VGG 特征,因此训练时该文件为必填项(train.py 中直接raise ValueError)。
4.3 训练超参数与阶段式损失调度
train.py 中的训练超参数:
| 参数 | 值 | 说明 |
|---|---|---|
random_seed | 0 | 随机种子 |
learning_rate | 1e-3 | Adam 学习率(判别器使用beta1=0.0) |
summary_freq | 20 | TensorBoard summary 写入频率 |
checkpoint_freq | 500 | checkpoint 保存频率 |
max_steps | 720000 | 最大训练步数 |
mlv.py 的build_train_graph()揭示了论文中的三阶段损失调度:
- 渲染损失(全程):对目标视角渲染图像施加基于 VGG 的感知损失(
render_loss),并带参考视锥外像素的 mask; - 环境贴图损失(24 万步之后启用):
envmap_loss通过tf.where(tf.greater(global_step, 240000), envmap_loss, 0.0)控制,即前期只训练 MPI 网络; - 对抗损失(69 万步之后启用):对合成环境贴图施加基于 SPADE 判别器结构的对抗损失(
adv_loss/disc_loss),实现细节见 nets.py 的discriminator()(含谱归一化 power iteration),同样通过tf.where(tf.greater(global_step, 690000), ...)调度。
此外,梯度处理采用denan(把 NaN 梯度置零)后按全局范数裁剪到 100(mlv.py)。训练过程中会输出丰富的 TensorBoard summary:各级环境贴图与球壳合成、MPI 各平面的 RGB/Alpha/PSV、梯度范数、cube 边长与中心位置等(mlv.py)。
五、核心模块源码结构速览
| 文件 | 职责 | 关键内容 |
|---|---|---|
| mlv.py | 训练/推理主类MLV | infer_mpi、mpi_render_view、predict_lighting_vol、render_envmap、build_train_graph、train、format_network_input |
| nets.py | 网络结构 | mpi_net(MPI 预测 3D U-Net)、cube_net_multires(多分辨率立方体细化)、discriminator(SPADE 风格判别器) |
| geometry/projector.py | 几何/投影工具 | inv_depths(逆深度采样)、mpi_resample_cube(MPI→立方体重采样,含三线性插值)、spherical_cubevol_resample(立方体→球面重采样)、over_composite(over 合成)、interleave_shells(按半径重排球壳) |
| geometry/sampling.py | 采样工具 | 三线性插值 gather(trilerp_gather)等 |
| data_loader.py | InteriorNet 数据管线 | ViewSequence/ViewTrip、prepare_training_set/prepare_eval_set、format_inputs、相机参数与位姿解析 |
| interiornet_test.py | 预训练模型测试入口 | 完整推理图 + npz 批处理 |
| train.py | 训练入口 | 超参数配置、VGG 权重加载、训练循环 |
几个值得注意的源码实现细节:
- 多尺度嵌套立方体:
predict_lighting_vol()依据scale_factors计算各级立方体的边长(cube_side_lengths = [2*max_depth, 2*max_depth/2, ...])与嵌套索引(cube_nest_inds),粗级立方体中由更细级覆盖的区域在渲染环境贴图前会被scatter_nd生成的 mask 置零(mlv.py 与 mlv.py),从而避免重复贡献; - 粗到细的级联输入:
cube_net_multires中每一级网络都接收上一级输出在对应子区域的切片上采样结果作为额外通道(tf.concat([tf.stop_gradient(inputs[i]), i_outvol_next], axis=-1)),并通过 sigmoid 权重把预测体积与输入体积做凸组合(nets.py); - 预乘 Alpha 插值:无论是 MPI→立方体还是立方体→球面重采样,均对预乘 alpha 后的 RGBA 做三线性插值,再反预乘还原(projector.py),这是避免边缘暗边的标准做法。
六、硬件与显存约束
README.md 的 Extra 一节给出了明确的硬件前提,作者在训练与测试中使用的是NVIDIA Tesla V100 GPU,且每个 minibatch 只有一个样本:
- 训练:建议≥16 GB显存;
- 测试:建议≥12 GB显存;
- 若在 <16 GB 的 GPU 上训练,可以尝试去掉多尺度表示中最细的一级体积。对应到源码,即修改 train.py 中的
scale_factors = [2, 4, 8, 16],注释也提示"try omitting 16 if you have GPU memory issues";类似地,depth_clip = 20.0需要按数据集实际深度范围调整(train.py)。
七、致谢与引用
Lighthouse 的代码库隶属于 Google Research 的共享仓库,README 提示不要为该仓库开 issue 跟踪 bug,使用过程中如有问题可直接邮件联系作者。
如果使用本代码,请按以下 BibTeX 引用(来自 README.md):
@article{Srinivasan2020, author = {Pratul P. Srinivasan, Ben Mildenhall, Matthew Tancik, Jonathan T. Barron, Richard Tucker, Noah Snavely}, title = {Lighthouse: Predicting Lighting Volumes for Spatially-Coherent Illumination}, journal = {CVPR}, year = {2020}, }结语
Lighthouse 以"MPI 中间表示 + 多尺度光照体积 + 体渲染"的技术路线,把二维图像输入转化为三维空间中任意位置的光照场,为空间一致照明估计提供了可复现的完整工程。本文从环境搭建、外部代码补全、预训练模型推理、自训练参数到核心源码实现进行了系统梳理。需要特别留意两点:一是两个外部非 Apache 许可代码块(nets.py 与 geometry/projector.py 末尾的说明)必须手动补全;二是显存门槛较高,低显存训练时应优先裁剪最细一级体积(调整scale_factors),并注意depth_clip需匹配自己的数据集深度范围。
【免费下载链接】google-researchGoogle Research项目地址: https://gitcode.com/gh_mirrors/go/google-research
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考