NPPE-3: Low-Light Denoising + 4x Super Resolution

A NAFNet-style restoration network that takes a low-resolution, noisy, low-light image and produces a 4x larger, denoised image in a single forward pass.

Trained from scratch for the DLP 26T2 NPPE-3 competition, where submissions are scored by PSNR on grayscale pixels of the restored images.

Task

Input Output
Resolution 160 x 256 640 x 1024
Condition noisy, low light denoised, 4x upscaled

Denoising and upscaling are handled jointly by one network rather than as two stages.

Architecture

  • 16 NAF blocks at width 64, operating at the low resolution
  • each block: LayerNorm β†’ 1x1 conv β†’ 3x3 depthwise conv β†’ SimpleGate β†’ simplified channel attention β†’ 1x1 conv, followed by a small gated feed-forward part
  • two PixelShuffle stages (2x each) for the 4x upsampling
  • a bilinear upscale of the input is added to the output, so the network only learns the correction rather than the whole image
  • 0.83 M parameters, no GAN and no perceptual loss, since both trade PSNR for sharpness

Data

1105 paired training images, 267 held out for validation, 60 test images. Inputs average a brightness of 43/255, and their targets 43.5/255, so the task is dominated by denoising and detail recovery rather than global brightening.

Training

Single Tesla T4, mixed precision, roughly 3.6 hours total.

Stage 1 β€” restoration (30,000 iterations, 155 min)

  • Charbonnier (smooth L1) loss on RGB
  • random 64x64 input crops (256x256 targets), random flips and 90Β° rotations
  • AdamW, lr 2e-4 with a cosine schedule to 1e-7, batch 16
  • EMA of the weights (decay 0.999); validation is scored with the averaged weights

Stage 2 β€” metric-aligned fine-tuning (6,000 iterations, 62 min)

  • loss switched to grayscale MSE + 0.1 Γ— Charbonnier, because the competition scores PSNR on grayscale rather than RGB
  • larger 128x128 crops for more context per step
  • AdamW, lr 5e-5 with a cosine schedule, batch 8
  • the checkpoint is only overwritten when validation PSNR actually improves

Results

Validation grayscale PSNR, measured on full images the way the competition scores them.

Checkpoint Val gray PSNR (20 images)
After stage 1 39.147 dB
After stage 2 39.213 dB

Final model on a larger 30-image validation subset:

Inference Val gray PSNR
Single pass 38.991 dB
8x self-ensemble (flips + rotations) 39.038 dB

The self-ensemble was the better of the two on identical images, so it was used for the test predictions. Both fine-tuning and the self-ensemble are small gains (+0.07 dB and +0.05 dB respectively) β€” the model had largely converged after stage 1.

Inference runs as a single pass over the whole 160x256 input, with no tiling. The image is small enough to fit in one forward pass, which also avoids blending seams between tiles.

Files

File Description
best_model.pth trained weights (state dict, 3.4 MB)
model.py model definition and a load_model helper

Usage

import numpy as np
import torch
from PIL import Image
from model import load_model

model = load_model("best_model.pth", device="cpu")

img = np.array(Image.open("low_light_input.png").convert("RGB"))
x = torch.from_numpy(img).permute(2, 0, 1).float().div(255).unsqueeze(0)

with torch.no_grad():
    out = model(x).clamp(0, 1)

out = (out[0].permute(1, 2, 0).numpy() * 255).round().astype(np.uint8)
Image.fromarray(out).save("restored.png")

Weights can also be pulled directly:

from huggingface_hub import hf_hub_download
path = hf_hub_download("vikas-06978/nppe3-lowlight-sr", "best_model.pth")

Limitations

  • trained only on this competition's data, so it expects the same degradation: low light plus sensor-like noise at a 4x downscale
  • fixed 4x factor β€” the PixelShuffle head cannot produce other scales
  • optimised purely for PSNR, so outputs are faithful but smoother than a GAN-based model would produce; fine textures are reconstructed conservatively

License

Apache 2.0.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support