一句话总结

BRF-GS 用混合 BRDF 驱动的高斯核替代传统辐射传输求解器,多角度高光谱图像生成速度比 DART 等 3D 辐射传输模型快约 1000x,同时保持更高的空间和光谱保真度。


为什么需要这个?

遥感中的双向反射因子(BRF)描述地表在特定太阳-观测几何下的反射特性——植被冠层的热点效应、城市建筑的镜面反射、裸土的各向异性散射,都编码在这个量里。问题在于获取多角度 BRF 数据极度昂贵:

传统 3D 辐射传输建模的三大瓶颈:

  • 需手工构建精细场景(树冠结构、叶片光学特性逐层建模)
  • Monte Carlo 光线追踪:DART 渲染单帧约 18 秒,生成训练集需要数天
  • 场景-传感器几何耦合,改变观测角度必须重新计算

3D Gaussian Splatting(3DGS)理论上是解法——用数百万 3D 高斯基元表示场景,渲染速度实时级别。但直接搬到 BRF 建模有两个硬伤:

问题 1:球谐函数(SH)表达能力不足

标准 3DGS 用 3 阶 SH(16 个系数)建模颜色的视角依赖性,能处理漫反射渐变,但对遥感场景中尖锐的热点峰(backscattering peak)和镜面反射瓣(specular lobe),需要极高阶 SH 才能拟合,计算量指数上升。

问题 2:高光谱维度爆炸

从 RGB(3 通道)扩展到高光谱(200 波段)时,每个高斯的 SH 系数量暴增:

配置 参数量(1M 高斯) GPU 显存
标准 3DGS(RGB,16系数) 48M 192 MB
朴素高光谱扩展(200波段,16系数) 3200M 12.8 GB
BRF-GS(9系数 + FP16) 900M 1.8 GB

BRF-GS 提出三个针对性解法:混合 BRDF 核、可靠波段选择、几何-光谱解耦训练。


核心原理

BRF 的物理定义

BRF 是无量纲量,定义为目标辐亮度与同等条件下朗伯体辐亮度之比:

\[\text{BRF}(\theta_i, \phi_i, \theta_r, \phi_r, \lambda) = \frac{L_r(\theta_r, \phi_r, \lambda)}{L_r^{\text{Lambertian}}(\theta_r, \phi_r, \lambda)}\]

它随波长 $\lambda$ 和四个角度参数变化。标准 3DGS 的 SH 只对视角方向 $(\theta_r, \phi_r)$ 建模,没有显式处理不同入射方向 $(\theta_i, \phi_i)$ 的差异——这正是遥感中最关键的信息。

混合 BRDF 核的设计

BRF-GS 的核心是把低阶 SH(负责漫反射背景)和物理 BRDF 模型(负责高频方向性)混合:

\[c_{\lambda}(\mathbf{d}) = \underbrace{\sum_{lm} k_{lm}^{\lambda} \, Y_l^m(\mathbf{d})}_{\text{低频漫反射(SH,2阶9系数)}} + \underbrace{w_{\lambda} \cdot f_{\text{GGX}}(\mathbf{d}, \mathbf{n}, \mathbf{l}, \alpha)}_{\text{高频镜面反射(Cook-Torrance)}}\]

$w_{\lambda}$ 是每个波段独立的镜面权重——植被 NIR(850nm)和 SWIR(2200nm)的镜面特性完全不同,不能共享一个 Fresnel 项。


代码实现

高斯基元数据结构

import torch
import torch.nn as nn

class BRFGaussian(nn.Module):
    def __init__(self, n: int, n_bands: int = 200, sh_deg: int = 2):
        super().__init__()
        sh_c = (sh_deg + 1) ** 2  # 2阶SH=9系数,降阶以节省显存

        # 几何属性(与标准3DGS相同)
        self.means      = nn.Parameter(torch.randn(n, 3))
        self.log_scales = nn.Parameter(torch.zeros(n, 3))
        self.quats      = nn.Parameter(torch.randn(n, 4))
        self.log_opac   = nn.Parameter(torch.full((n,), -3.0))

        # 高光谱漫反射 SH:(N, n_bands, sh_c),用 fp16 减半显存
        self.sh_diff = nn.Parameter(torch.zeros(n, n_bands, sh_c, dtype=torch.float16))

        # BRDF 参数(每个高斯一组,共享粗糙度跨波段)
        self.roughness   = nn.Parameter(torch.full((n,), 0.5))  # [0,1]
        self.metallic    = nn.Parameter(torch.zeros(n))
        # 各波段独立镜面权重,捕捉光谱 Fresnel 差异
        self.spec_weight = nn.Parameter(torch.zeros(n, n_bands))

Cook-Torrance GGX BRDF 核

def cook_torrance_specular(
    n_dot_l: torch.Tensor,   # (N,) 法线·光源方向
    n_dot_v: torch.Tensor,   # (N,) 法线·视线方向
    n_dot_h: torch.Tensor,   # (N,) 法线·半程向量
    roughness: torch.Tensor, # (N,)
) -> torch.Tensor:
    """返回镜面项标量 (N,),Fresnel 由 spec_weight 逐波段处理"""
    alpha  = roughness ** 2
    alpha2 = alpha ** 2

    # GGX 法线分布函数 D
    denom = (n_dot_h ** 2) * (alpha2 - 1.0) + 1.0
    D = alpha2 / (torch.pi * denom ** 2 + 1e-7)

    # Smith-Schlick 几何遮蔽 G
    k   = (roughness + 1.0) ** 2 / 8.0
    G_l = n_dot_l / (n_dot_l * (1.0 - k) + k + 1e-7)
    G_v = n_dot_v / (n_dot_v * (1.0 - k) + k + 1e-7)

    return D * G_l * G_v / (4.0 * n_dot_l * n_dot_v + 1e-7)

为什么省略 Fresnel 项? 遥感波段跨越 400–2500nm,不同波段的折射率差异极大,Schlick 近似在短波红外严重失准。用逐波段的 spec_weight[λ] 建模效果更好,参数量也更少。

几何可靠波段选择

def select_reliable_bands(
    cube: torch.Tensor,        # (H, W, C) 高光谱数据立方体
    wavelengths: torch.Tensor, # (C,) 波长,单位 nm
    n_select: int = 10,
) -> list[int]:
    """选择 SNR 高且无大气吸收的波段用于 SfM 初始化"""
    # 排除水汽、CO₂ 吸收带
    bad = (
        ((wavelengths > 1350) & (wavelengths < 1450)) |
        ((wavelengths > 1800) & (wavelengths < 1960)) |
        (wavelengths > 2450)
    )
    valid = torch.where(~bad)[0]

    # 局部均值/标准差估计 SNR(5×5 滑窗)
    snr_scores = []
    for i in valid:
        b = cube[:, :, i].float()
        patches = b.unfold(0, 5, 1).unfold(1, 5, 1)
        snr_scores.append((patches.mean((-1,-2)) / (patches.std((-1,-2)) + 1e-6)).mean().item())

    top_k = torch.tensor(snr_scores).topk(n_select).indices
    return valid[top_k].tolist()

两阶段解耦训练

def train_brf_gs(model: BRFGaussian, dl, n_bands: int, selected: list[int]):
    geom_params = [model.means, model.log_scales, model.quats, model.log_opac]
    spec_params = [model.sh_diff, model.roughness, model.metallic, model.spec_weight]

    # ── Stage 1:几何优化,仅用选定高 SNR 波段 ──────────────
    opt1 = torch.optim.Adam(geom_params, lr=1.6e-4)
    for step, (rays, gt) in enumerate(dl.stage1):
        loss = l1_ssim_loss(render(model, rays, selected), gt)
        opt1.zero_grad(); loss.backward(); opt1.step()
        if step % 100 == 0:
            adaptive_density_control(model)  # 标准3DGS致密化/剪枝

    # ── Stage 2:冻结几何,分波段批处理光谱建模 ──────────────
    for p in geom_params:
        p.requires_grad_(False)

    # 各参数组独立学习率,防止 sh_diff 梯度淹没 BRDF 参数
    opt2 = torch.optim.Adam([
        {"params": model.sh_diff,    "lr": 1e-4},
        {"params": model.spec_weight, "lr": 5e-4},
        {"params": [model.roughness, model.metallic], "lr": 1e-3},
    ])

    BAND_BATCH = 20  # 每批 20 波段,将显存峰值从 58GB → 24GB
    for rays, gt_hyper in dl.stage2:
        total_loss = 0.0
        for b in range(0, n_bands, BAND_BATCH):
            bands = list(range(b, min(b + BAND_BATCH, n_bands)))
            total_loss += l1_loss(render(model, rays, bands), gt_hyper[..., bands])
        opt2.zero_grad(); total_loss.backward(); opt2.step()

性能实测

测试环境:NVIDIA A100 80G,CUDA 12.2,AIR-BRF 数据集(200 波段,3 个场景含自然与人工目标)

方法 渲染速度 训练时间 PSNR (dB) 光谱角 (°)
DART(3D 辐射传输) ~18 s/帧 N/A
标准 3DGS(SH only) 12 ms/帧 1.5 h 28.3 4.7
BRF-GS(论文报告) 18 ms/帧 2.8 h 31.6 2.1

数据来自论文实验。渲染速度比 DART 快约 1000x;光谱角(SAM)越小代表光谱形状越准确

关键观察:

  • BRDF 镜面项对植被热点区域贡献最大,该区域 PSNR 相比纯 SH 提升约 4 dB
  • BAND_BATCH=20 将显存峰值从 58GB 压到 24GB,单卡 A100 可完成完整训练
  • Stage 2 的分组学习率对收敛速度有 1.4x 加速

什么时候用 / 不用?

适用场景 不适用场景
多角度航空/机载高光谱 BRF 建模 水体/平静海面(高斯密度不适合强镜面主导)
替代 DART 生成多角度训练数据 单角度高光谱(几何约束不足,无法重建)
城市/植被场景定量遥感 需要严格 SI 可溯源的辐射校正产品
传感器模拟与数据增强 实时机载处理(18ms/帧含显存传输)

常见坑与调试

坑 1:Stage 1 未收敛就进入 Stage 2

几何不准时 Stage 2 的光谱会严重过拟合训练视角:

# 用选定波段的 PSNR 作为 Stage 1 收敛信号
if compute_psnr(render(model, val_rays, selected), val_gt) > 27.5 and step > 15000:
    switch_to_stage2()

坑 2:大气吸收带污染几何初始化

如果不做波段筛选,直接用所有波段做 SfM,SNR 极低的吸收带会破坏特征匹配,导致点云稀疏。select_reliable_bands 这步不能省。

坑 3:sh_diff 使用 fp16 时的梯度下溢

# 错误:fp16 前向 + fp32 优化器,梯度可能下溢至 0
# 正确:用 GradScaler
scaler = torch.amp.GradScaler()
with torch.amp.autocast("cuda"):
    loss = compute_loss(model, rays, gt)
scaler.scale(loss).backward()
scaler.step(opt2); scaler.update()

延伸阅读

  • 原论文:BRF-GS arXiv:2608.31159
  • 3DGS 原始论文:Kerbl et al., “3D Gaussian Splatting for Real-Time Radiance Field Rendering”, SIGGRAPH 2023
  • BRF 物理定义权威综述:Schaepman-Strub et al., “Reflectance Quantities in Optical Remote Sensing”, Remote Sensing of Environment, 2006
  • DART 辐射传输模型(对照基线):dart.omp.eu

进阶方向:将 BRF-GS 与 NeRF-based 方法(如 Instant-NGP)比较,或引入时序维度建模植被物候变化——后者需要在高斯生命周期管理上做额外设计。