Official implementation of BBRD (Brightness Bias-Robust Denoising), ECCV 2026.
Sungjun Cho, Zhuangzhuang Chen, Xiaomeng Li · HKUST
TL;DR Under signal-dependent camera noise, MSE-trained denoisers reconstruct dark pixels up to 6× worse than their per-band noise floor. BBRD is a drop-in MSE replacement — GMM brightness bands + empirical per-band noise normalization + softmax Group-DRO — that fixes this with zero added parameters and zero inference cost, and is the only loss (of 13 tested) that improves every brightness band at once.
Per-band PSNR during training (NAFNet, SIDD). Same model, data, and bands — only the
loss differs. MSE locks in a 1.3 dB dark/bright gap that even widens; BBRD closes it to 0.8 dB.
For a clean image, signal-dependent (Poisson–Gaussian) noise has variance
σ²(I) = αI + β. Dark regions have small absolute noise but far lower SNR, so they
are intrinsically hard. MSE training makes it worse for two compounding reasons:
- Signal-dependent residuals — near the noise floor
E[r²] ≈ Var(n)grows with brightness. - Brightness-correlated Jacobian — higher input activations yield larger
‖∂f/∂θ‖.
Both factors grow with brightness, so bright pixels dominate gradient updates and dark regions stay chronically under-optimized. This holds across 8 architectures (CNNs, Transformers, SSMs) — it is a loss-level problem, not a model-capacity one.
Left: the brightest band's Jacobian norm exceeds the darkest across all architectures (SIDD).
Right: BBRD pushes the PSNR-D vs PSNR-B Pareto frontier strictly outward.
BBRD corrects brightness bias in three sequential steps (removing any one breaks it):
1. Brightness bands (GMM + Gaussian soft assignment).
Compute a smoothed luminance map Ī = AvgPool₇ₓ₇(gray(y)). Fit a 1-D GMM to training-set
intensities and pick K by BIC (K=5 for SIDD, K=3 for PolyU), placing band edges at the
valleys between components. Assign each pixel softly,
φ_ik = exp(−(Ī_i − c_k)² / 2σ_g²) with σ_g = 0.05, to avoid boundary discontinuities.
2. Empirical noise normalization. Normalize per-band error by the empirical per-band noise variance — estimated once over the training set and held fixed (a data property, not a learned parameter), with no parametric noise model:
MSE_k = Σ_i φ_ik (ŷ_i − y_i)² / Σ_i φ_ik
σ²_k = Σ_i φ_ik (x_i − y_i)² / Σ_i φ_ik # x = noisy, y = clean
R_k = MSE_k / σ²_k
This is the step that turns reweighting from a bias-amplifier into a bias-corrector: without it, DRO upweights the noisiest (bright) bands and worsens the gap.
3. Softmax Group-DRO. Dynamically upweight whichever band is currently worst on the noise-corrected scale:
L_BBRD = Σ_k w_k · R_k , w_k = softmax(η · R_k) , η = 5
Band assignment and DRO are training-only; the deployed model is bitwise identical to one
trained with vanilla MSE. See comparison_losses.py::BBRDLoss.
Brightness band construction: input → smoothed brightness map → GMM (valleys = boundaries)
→ Gaussian soft weights φ_ik.
BBRD/
├── train_bbrd.py # Train BBRD or any baseline loss, any backbone
├── eval_per_band.py # PSNR-D / PSNR-B / aggregate per-band evaluation
├── comparison_losses.py # BBRDLoss (ours) + the 13 paper baselines
├── models/ # nafnet, residual_unet, restormer (drop-in backbones)
├── datasets_module/ # paired dataset + signal-dependent noise
├── utils/
│ ├── gmm_bands.py # GMM + BIC brightness-band edges
│ ├── evaluation.py # brightness-stratified per-band metrics
│ └── utils.py
├── scripts/
│ ├── precompute_gmm_edges.py
│ └── run_example.sh
├── assets/ # paper figures
└── requirements.txt
git clone https://github.com/xmed-lab/BBRD.git # or the mirror: https://github.com/Sungjun01/BBRD.git
cd BBRD
pip install -r requirements.txtTested with Python 3.12 and PyTorch ≥ 2.0 (CUDA). scikit-learn is recommended for GMM band
fitting (a pure-NumPy fallback is included).
DenoisingDataset expects paired clean/noisy folders per split:
datasets/SIDD_256/
├── train/{clean, noisy}/ # 'clean' or 'gt'
└── val/{clean, noisy}/
We evaluate on SIDD (smartphone, dark-skewed) and PolyU (DSLR, bright-skewed, ~18.5× bright/dark energy ratio). Download from the official sources and arrange as above. Pre-trained weights are not included.
Brightness distribution across SIDD, PolyU, and LumaSet-700.
# BBRD on NAFNet / SIDD: GMM bands (BIC capped at K=5) + empirical variance + Gaussian soft DRO
CUDA_VISIBLE_DEVICES=0 python train_bbrd.py \
--loss bbrd --backbone nafnet \
--dataset_root datasets/SIDD_256 \
--bbrd_gmm --bbrd_gmm_k_max 5 \
--band_type gaussian --eta 5.0 \
--exp_name bbrd_nafnet_sidd
# MSE baseline (identical settings — for the per-band comparison)
CUDA_VISIBLE_DEVICES=0 python train_bbrd.py \
--loss mse --backbone nafnet \
--dataset_root datasets/SIDD_256 --exp_name mse_nafnet_sidd- Backbone-agnostic:
--backbone {nafnet, residual_unet, restormer}. BBRD plugs into any denoiser whose forward ismodel(noisy) -> denoised; the paper additionally reports DRUNet, KBNet, SCUNet, SwinIR, MambaIR, and MambaIRv2 using the same loss with no per-backbone tuning. - Any baseline via
--loss {l1, charbonnier, huber, ssim, msssim, fft, l1_fft, focal_freq, gfl, focal, ohem, uncertainty, inv_variance}. - Optionally precompute GMM edges once for parallel runs:
python scripts/precompute_gmm_edges.py --dataset_root datasets/SIDD_256 --k_max 5.
CUDA_VISIBLE_DEVICES=0 python eval_per_band.py --dataset sidd \
--backbone nafnet \
--checkpoint outputs/bbrd_nafnet_sidd/checkpoints/best.pt \
--dataset_root datasets/SIDD_256 --split valReports PSNR-D (darkest band), PSNR-B (brightest band), aggregate PSNR/SSIM, and the full
per-band breakdown. Protocol: SIDD uses [0,0.2) / [0.8,1.0]; PolyU uses [0,0.45) / [0.7,1.0].
Loss comparison (NAFNet). BBRD is the only method that improves both the dark and bright bands on both datasets. Best in bold.
| Method | SIDD PSNR-D | SIDD PSNR-B | SIDD PSNR | PolyU PSNR-D | PolyU PSNR-B | PolyU PSNR |
|---|---|---|---|---|---|---|
| MSE (ℓ2) | 37.51 | 38.37 | 39.47 | 35.80 | 37.82 | 38.50 |
| ℓ1 | 37.60 | 37.67 | 39.65 | 35.94 | 37.87 | 38.68 |
| Charbonnier | 37.48 | 38.66 | 39.65 | 35.99 | 38.18 | 38.79 |
| ℓ1+FFT | 37.70 | 38.28 | 39.80 | 36.11 | 38.20 | 38.80 |
| Focal | 37.19 | 38.55 | 39.16 | 35.54 | 37.48 | 38.07 |
| OHEM | 37.47 | 38.64 | 39.44 | 36.08 | 37.92 | 38.62 |
| Heterosc. NLL | 37.29 | 38.09 | 39.37 | 36.09 | 38.12 | 38.75 |
| BBRD (ours) | 37.96 | 38.69 | 40.12 | 36.27 | 38.34 | 39.06 |
(Full 13-baseline table — incl. Huber, SSIM, MS-SSIM, FFT, Focal Freq., Guided Freq. — is in the paper.)
Architecture generalization (MSE → BBRD, SIDD aggregate PSNR). Same loss, no per-backbone tuning:
| NAFNet | DRUNet | KBNet | SwinIR | Restormer | SCUNet | MambaIR | MambaIRv2 | |
|---|---|---|---|---|---|---|---|---|
| MSE | 39.47 | 40.04 | 39.82 | 39.48 | 40.19 | 40.34 | 40.09 | 40.04 |
| BBRD | 40.12 | 40.46 | 40.41 | 40.23 | 40.72 | 40.54 | 40.26 | 40.52 |
BBRD delivers up to +0.45 dB on dark bands, +0.32 dB on bright bands, and +0.65 dB aggregate on SIDD, with the largest gains in the darkest regions.
Qualitative comparison (SIDD / PolyU). BBRD corrects dark-region residuals MSE leaves behind
(green = improvement; red = regression, confined to bright areas where MSE already does well).
LumaSet-700. A synthetic benchmark of 700 patches stratified uniformly across 5 brightness quintiles, rendered under signal-dependent / Gaussian / random-variance noise at 11 severity levels. BBRD beats MSE across all noise types (largest gain +0.51 dB under signal-dependent noise), showing the fix generalizes beyond heteroscedastic noise. (To be released.)
@inproceedings{cho2026bbrd,
title = {The Devil is in the Dark Pixels: Toward Brightness Bias-Robust Denoising},
author = {Cho, Sungjun and Chen, Zhuangzhuang and Li, Xiaomeng},
booktitle = {European Conference on Computer Vision (ECCV)},
year = {2026}
}Backbone architectures build on the public implementations of NAFNet and Restormer. We thank the authors of the SIDD and PolyU datasets.
Released under the MIT License.