Skip to content

Repository files navigation

EfficientDeblurNet — NTIRE 2026 Real-World Deblurring

A lightweight, production-ready image deblurring model designed to meet all NTIRE 2026 challenge constraints.

Constraint Limit Model
Parameters ≤ 5M ~2.5M
Compute ≤ 200 GMACs ~100 GMACs
FHD Inference < 1s < 200ms GPU

Architecture

EfficientDeblurNet is a 3-level U-Net encoder-decoder built from efficient blocks:

  • SimpleGate — zero-parameter activation (element-wise product of split channels)
  • DWConvBlock — depthwise-separable conv + LayerNorm + SimpleGate + residual
  • ChannelAttentionLite — GAP + 1×1 × 2 + Sigmoid (no MLP overhead)
  • PixelShuffleUp — checkerboard-free 2× upsampling
  • StrideDown — learned strided DW-conv downsampling
Input(3ch) → Stem(32ch)
Encoder: [32→64→128→256ch]  with StrideDown
Bottleneck: 4× EfficientBlock @ 256ch
Decoder: [256→128→64→32ch]  with PixelShuffleUp + skip-cat
Head: LayerNorm → Conv3×3 → +input_residual

Project Structure

deblur_ntire2026/
├── models/
│   ├── blocks.py          # DWConvBlock, SimpleGate, ChannelAttentionLite, EfficientBlock
│   └── deblur_net.py      # EfficientDeblurNet, build_model()
├── dataset/
│   ├── rsblur_dataset.py  # RSBlur, GoPro, RealBlur, PairedImageDataset loaders
│   └── augmentations.py   # PairedAugment, ValTransform
├── training/
│   ├── losses.py          # CharbonnierLoss, PerceptualLoss, FFTLoss, CombinedLoss
│   ├── trainer.py         # Trainer (AMP, AdamW, cosine LR, grad clip, TensorBoard)
│   ├── scheduler.py       # build_scheduler (cosine_warmup, step)
│   └── distillation.py    # DistillationLoss, DistillationTrainer (bonus)
├── evaluation/
│   ├── metrics.py         # compute_psnr, compute_ssim, LPIPSMetric
│   └── evaluate.py        # Batch evaluation script
├── inference/
│   ├── infer.py           # Tiled + direct inference, wall-clock timing
│   ├── export.py          # TorchScript + ONNX export
│   └── tta.py             # 4-flip / 8-rot test-time augmentation (bonus)
├── utils/
│   ├── config.py          # YAML config loader
│   ├── logger.py          # Python logging + TensorBoard writer
│   ├── efficiency.py      # thop FLOPs/params counter
│   └── pruning.py         # L1 unstructured pruning hooks (bonus)
├── configs/
│   ├── train_config.yaml  # Full training hyperparameters
│   └── model_config.yaml  # Model architecture config
├── train.py               # Training entry point
├── eval.py                # Evaluation entry point
├── infer_single.py        # Single/batch inference with timing
└── efficiency_check.py    # Parameter & FLOP compliance checker

Setup

pip install -r requirements.txt

Key dependencies: PyTorch ≥ 2.1, torchvision ≥ 0.16, PyYAML, thop, lpips, tensorboard


Data Preparation

RSBlur (Challenge Primary)

data/rsblur/
  train/input/    # blurry PNGs
  train/target/   # sharp PNGs
  val/input/
  val/target/

GoPro

data/gopro/
  train/GOPR0xxx/blur_gamma/*.png
  train/GOPR0xxx/sharp/*.png
  test/...

Update configs/train_config.yaml:

data:
  dataset: rsblur      # or gopro / generic
  root: data/rsblur

Training

# Standard training (300 epochs, cosine LR warmup)
python train.py --config configs/train_config.yaml

# Resume from checkpoint
python train.py --config configs/train_config.yaml --resume checkpoints/epoch_0100.pth

# Debug smoke test (2 iterations, 1 epoch)
python train.py --config configs/train_config.yaml --debug

# TensorBoard monitoring
tensorboard --logdir logs/

Training defaults:

  • Optimizer: AdamW (lr=2e-4, wd=1e-4, β=(0.9, 0.9))
  • LR schedule: Cosine annealing with 5-epoch warmup
  • Loss: Charbonnier (1.0) + Perceptual/VGG (0.05) + FFT (0.1)
  • Mixed precision: FP16 (torch.cuda.amp)
  • Grad clipping: max_norm=1.0
  • Batch size: 8 × 256×256 crops

Evaluation

python eval.py \
  --config configs/train_config.yaml \
  --checkpoint checkpoints/best.pth \
  --data_dir data/rsblur/val \
  --device cuda

Outputs: mean PSNR (dB), SSIM, LPIPS per image and aggregated.


Inference

Single image

python infer_single.py \
  --input data/test/blur.png \
  --output results/sharp.png \
  --checkpoint checkpoints/best.pth \
  --config configs/train_config.yaml \
  --device cuda

Batch folder

python infer_single.py \
  --input_dir data/test/blur/ \
  --output_dir results/ \
  --checkpoint checkpoints/best.pth \
  --device cuda

With Test-Time Augmentation

python infer_single.py --input blur.png --output sharp.png \
  --checkpoint checkpoints/best.pth --tta flip4

Tiled inference is enabled by default (tile=512, overlap=32) to handle FHD on low-VRAM GPUs.


Efficiency Verification

# Check params, GMACs and FHD timing
python efficiency_check.py --device cuda

# CPU timing (for reproducibility)
python efficiency_check.py --device cpu

Expected output:

Parameters   : 2.50M  ✅  [limit: 5.00M]
GMACs (FHD)  : ~100   ✅  [limit: 200]
Inference    : ~180ms ✅  [limit: 1000ms]

Model Export

TorchScript

python inference/export.py \
  --checkpoint checkpoints/best.pth \
  --format torchscript \
  --output model_ts.pt

ONNX (opset 17)

python inference/export.py \
  --checkpoint checkpoints/best.pth \
  --format onnx \
  --output model.onnx

Both modes verify the exported model reproduces the same output (max error < 1e-4).


Knowledge Distillation (Optional)

Use a larger teacher model (e.g., NAFNet) to boost student quality:

# In train_config.yaml
distillation:
  enabled: true
  teacher_checkpoint: path/to/teacher.pth
  distill_weight: 0.5

Model Pruning (Optional)

from utils.pruning import prune_model, remove_pruning
prune_model(model, amount=0.3)   # 30% L1 unstructured pruning
remove_pruning(model)             # make pruning permanent

License

This code is intended for the NTIRE 2026 challenge. See challenge rules for usage terms.

About

In this challenge, we focus on real-world images, and only efficient methods to restore them and add details.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages