Image restoration

Image restoration takes a degraded image and returns a clean one. LibreYOLO exposes it as the restore task, which covers denoising, deblurring and super-resolution behind a single output contract: one RGB image in, one RGB image out.

Definition

The restore task maps one image to another image. Denoising, deblurring and super-resolution are all the same task here, because they share one contract: the model consumes an RGB image and returns an RGB image, and the degradation it was trained to undo is a property of the checkpoint rather than of the API.

A prediction fills result.restored, a RestoredImage payload holding an (H, W, 3) uint8 RGB array. .array returns it as NumPy and .save(path) writes it to disk. result.restore_scale records the upscale factor the output canvas carries, which is 1 for a checkpoint that preserves resolution. result.boxes stays empty, so conf, iou and max_det are accepted for signature parity but have no effect, and save=True writes the restored image directly rather than an annotated photo.

Models

Three families serve restore, split by the degradation they undo.

NAFNet is the denoiser, and the only restore family LibreYOLO can train. Its architecture replaces the nonlinear activations of a UNet block with elementwise multiplication, and the published checkpoint is trained on SIDD real-image noise. Output stays at the input resolution.

Real-ESRGAN is the practical upscaler: three checkpoints trained against synthetic degradations rather than only bicubic downscaling, at 4x, 2x, and a smaller, faster 4x generator built for lower latency.

SwinIR upscales 4x with a Swin Transformer backbone, in three sizes covering the official lightweight generator and two real-world generators.

Predict

Weights download from Hugging Face on first use and are cached locally.

Upscale an image
from libreyolo import LibreYOLO, SAMPLE_IMAGE # The compact 4x generator; tile bounds peak memory on a large source.model = LibreYOLO("LibreRealESRGANx4t-restore.pt")result = model(SAMPLE_IMAGE, tile=512, tile_pad=10) result.restored.save("upscaled.png")print(result.restored.array.shape)   # 4x the input in each axis
Denoise an image
from libreyolo import LibreYOLO, SAMPLE_IMAGE # Trained on SIDD real-image noise; output stays at the input size.model = LibreYOLO("LibreNAFNetl-restore-sidd.pt")result = model(SAMPLE_IMAGE) result.restored.save("denoised.png")print(result.restore_scale)   # 1: no upscale for this checkpoint

Restoration runs at the source image's own resolution rather than a fixed network canvas, padding only to the network's downsample factor, so both time and memory scale with the pixel count of your input. tile splits the forward pass into overlapping tiles and blends the seams back together, and tile_pad is the halo added around each tile before it is cropped back out; both are Python keyword arguments. See prediction for sources, streaming and result handling.

Dataset format

Restoration pairs each degraded input image with a clean target image of exactly the same resolution, matched by filename stem.

dataset/
  data.yaml
  inputs/
    train/photo.jpg
    val/photo.jpg
  targets/
    train/photo.jpg
    val/photo.jpg
yaml
path: dataset
train: inputs/train
val: inputs/val
input_dir: inputs
target_dir: targets
degradation: denoise
dataset: MyDataset
nc: 1
names: {0: image}

nc and names are schema placeholders; a restore model returns Results.restored, not detections. degradation and dataset are optional provenance labels. target_stem_suffix covers datasets that name the clean image differently from its degraded pair. Validation keeps native resolution and pads only enough to stack a batch, so the metrics are computed on the original canvas. See dataset formats for the full contract.

Train

NAFNet is the only restore family with a training implementation. Real-ESRGAN.train() and SwinIR.train() both raise NotImplementedError: those checkpoints come from GAN training over synthetic degradation pipelines, and the paired restore trainer would run without reproducing that recipe.

Fine-tune NAFNet on paired images
from libreyolo import LibreYOLO model = LibreYOLO("LibreNAFNetl-restore-sidd.pt")model.train(data="my-dataset.yaml", epochs=100, imgsz=256, batch=16, lr0=1e-3)
Record the provenance on the checkpoint
from libreyolo import LibreYOLO model = LibreYOLO("LibreNAFNetl-restore-sidd.pt") # degradation and dataset are written into the saved checkpoint for# provenance; they take no part in training.model.train(    data="my-dataset.yaml",    epochs=100,    degradation="denoise",    dataset="MyDataset",)

The trainer takes coupled crops of the input and target pair, so both sides stay aligned. See training for datasets, multi-GPU and loggers, and the NAFNet page for this family's defaults and the inference-time pooling it detaches while training.

Validate

val() compares the restored output against the clean target, in RGB, on the original canvas, with no border crop and no resizing.

Validate and read the metric keys
from libreyolo import LibreYOLO model = LibreYOLO("LibreNAFNetl-restore-sidd.pt") # val() returns a plain dict, not an object.metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/PSNR"])   # fitnessprint(metrics["metrics/SSIM"])

metrics/PSNR is the peak signal-to-noise ratio in decibels, and it is also fitness, the number best-checkpoint selection reads. metrics/SSIM is structural similarity in [0, 1], computed with an 11x11 Gaussian window at sigma 1.5 and averaged over the three color channels. Higher is better for both.

Export

An exported restore model loads back through LibreYOLO() on its file suffix, so a .onnx or .engine file behaves like a checkpoint and returns the same Results, with restored carrying the output image.

Export
from libreyolo import LibreYOLO model = LibreYOLO("LibreNAFNetl-restore-sidd.pt") # imgsz is fixed into the graph, so pass the size your deployment# actually feeds the model.model.export(format="onnx", imgsz=256)
Run the exported file
from libreyolo import LibreYOLO, SAMPLE_IMAGE # The factory routes on the file suffix, so an exported artifact loads# like any checkpoint and returns the same Results object.model = LibreYOLO("LibreNAFNetl-restore-sidd.onnx")result = model(SAMPLE_IMAGE) result.restored.save("denoised.png")

Restore export fixes the spatial resolution into the graph, so pass the imgsz your deployment will actually feed the model. For NAFNet that size must divide by the network's downsample factor, and only the batch dimension stays dynamic under dynamic=True. For Real-ESRGAN and SwinIR, leaving imgsz out falls back to a small internal patch size rather than your working resolution. Per-format coverage is on each model page and in the full export matrix. Export lists the arguments every format accepts.

Verified against LibreYOLO v1.5.0.