SRNet โ€” Low-Light Denoising + 4x Super-Resolution

Submission model for DLP 26T2 NPPE3. Takes a dark, noisy 256x160 RGB image and produces a clean, correctly exposed 1024x640 RGB image in a single forward pass โ€” denoising, brightening, and 4x upscaling are learned jointly.

Results

PSNR
Kaggle public LB (42% of test set) 39.611 dB
Held-out val, 48 images, x8 ensemble 38.986 dB
Bicubic upsample baseline 33.424 dB

The competition metric is PSNR on PIL.Image.convert('L') output sampled every 8th pixel, so all figures above are computed that way.

Architecture

RCAN-style residual channel-attention network.

  • Input stem: the LR image is concatenated with a gamma-corrected copy (x ** 0.4545), giving 6 input channels. Low-light pixels cluster near 0 where the raw signal is poorly conditioned; the gamma copy hands the first convolution a well-spread version of the same data.
  • Body: N residual channel-attention blocks (RCAB), each a conv-ReLU-conv-SE stack with a 0.2-scaled residual, wrapped in a long skip connection.
  • Upsampler: two PixelShuffle x2 stages.
  • Output: the final conv bias is initialised to the training-set mean RGB, so the network does not spend its first thousands of updates learning a DC brightness offset through the residual stack.

This checkpoint: 64 channels, 24 RCAB blocks, 2.12 M parameters.

Width and depth were selected at runtime by a probe that timed three candidate configurations on the actual T4 and picked the largest that still fit the iteration budget (64ch reached 7.32 it/s; 96ch and 128ch were too slow to converge in the time available). load_model() reads both back from the checkpoint, so it reconstructs the right size automatically.

Training

  • Loss: Charbonnier on RGB plus an equal-weight Charbonnier on ITU-R 601-2 luma (0.299 / 0.587 / 0.114). The competition metric scores PIL.Image.convert('L') output, so luma error is what is actually graded.
  • Optimiser: Adam (0.9, 0.99), cosine schedule with linear warmup, gradient clipping at 1.0.
  • Precision: AMP fp16 with channels_last, on a single Kaggle T4.
  • Weights: EMA of the training weights, decay 0.999. Checkpointed on best validation PSNR.
  • Inference: x8 dihedral self-ensemble.
  • Run: 68,026 iterations at batch 16 on 64x64 LR crops (~985 epochs over 1,105 training pairs) in 2h54m on a single T4. Best checkpoint at iteration 62,000; validation PSNR was flat from ~50k onward.

Usage

import numpy as np
from PIL import Image
from model import load_model, enhance

model = load_model('best.pt', device='cuda')

lr = np.array(Image.open('test_00000.png').convert('RGB'))   # 256x160
hr = enhance(model, lr, device='cuda')                       # 1024x640
Image.fromarray(hr).save('out.png')

Files

File Description
best.pt EMA weights + best val PSNR + iteration count
model.py Architecture, checkpoint loader, inference helper
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