Aerial Film Restorer

Measured-fidelity restoration of scanned historical aerial survey film. Mid-century aerial archives reach the reader as flattened, patchworked mosaics โ€” per-plate exposure steps, vignette haze, lost local contrast, film grain, the accumulated damage of print, storage and scanning. This model reverses that damage without inventing a pixel of content, and โ€” unusually for the genre โ€” its fidelity is a measured quantity: every number and every image on this card comes from held-out photograph pairs with professionally-produced ground truth that the training never saw.

raw scan โ†’ this model โ†’ the archive's own professional clean

Bourges, IGN survey flight (1950s) โ€” a held-out pair. Left: the raw scanned mosaic as archives publish it, a plate seam running through the medieval fabric. Middle: this model โ€” the seam is gone and the city reads street by street. Right: the professionally cleaned orthophoto IGN produced from the same photographs, the ground truth every number on this card is measured against.

The measured claim

Restoration models usually argue with adjectives. This one argues with three numbers, each an energy ratio against ground truth where 1.00 means exactly what the negative holds, evaluated on 40 reserved pairs (mean ยฑ s.e., shown here on the 22 pairs additionally unseen by checkpoint selection):

axis value meaning at 1.00
detail on structure 1.02 ยฑ 0.03 every real edge as sharp as truth โ€” no smoothing, no fake acutance
detail in flat film 1.07 ยฑ 0.05 grain neither amplified nor scrubbed
large-scale tone 1.01 ยฑ 0.07 the ground's own tonal variation preserved โ€” no "uniform grey"

The known failure modes of naive training โ€” aliased targets that manufacture sharpness, adversaries that trade grain for punch, losses that flatten tone โ€” were each identified by measurement and removed. What remains restores, and only restores.

Bordeaux: raw โ†’ model โ†’ IGN professional clean

Bordeaux โ€” likewise held out. Vignette haze lifts into street-level legibility; every roofline stays exactly where the film put it.

Warsaw 1944: served archive โ†’ model

Warsaw, June 1944 โ€” a wartime reconnaissance print (GUGiK archival aerial), fully outside the training distribution: no historical Warsaw imagery has ground truth. The scan is shown AS SCANNED โ€” before any correction; the target annotations are part of the print's face and are treated as content. The window is cropped to the photograph, as the limitations advise โ€” off-white mount paper counts as content to the no-evidence shield.

Warsaw, 4 November 1944: as scanned โ†’ model

Warsaw city centre, 4 November 1944 โ€” after the city's fall, during its systematic razing. Captured German reconnaissance film (US National Archives, RG 373, public domain by age), orthorectified by the Warsaw Rising Museum. The scan as scanned โ†’ the model: the blotched exposure evens and block after block of roofless shells becomes individually legible โ€” the record of the destruction, made readable without one invented pixel.

Warsaw 1951: served archive โ†’ model

Warsaw, 1951 โ€” the reconstruction-era survey (City of Warsaw open data, ยฉ m.st. Warszawa) at whole-city zoom, the native mosaic as scanned: dozens of survey plates, each with its own exposure. The patchwork resolves into one continuous city โ€” including across the river โ€” while every block stays what the film recorded.

Processing aerials

pip install -r requirements.txt
python inference.py scan.tif restored.png                     # the model
python inference.py scan.tif restored.png --weights model-prof.safetensors

As a library, over a folder:

import glob
import numpy as np
from PIL import Image
from inference import load_model, read_image, restore

net = load_model("model.safetensors")          # picks cuda / mps / cpu itself
for path in sorted(glob.glob("archive/*.tif")):
    out = restore(net, read_image(path))       # float32 [0,255], input-sized
    Image.fromarray(out.astype(np.uint8)).save(path.replace(".tif", "-restored.png"))

What the machinery does for you, and what it expects of you:

  • Input: single-channel film imagery, ~0.4โ€“13 m/px, any size โ‰ฅ 64 px. read_image handles 8-bit and 16-bit files (PIL's convert("L") clips 16-bit scans โ€” use the provided reader) and reduces colour to luminance.
  • Five channels from one image: the model wants registered 2ร—/4ร— context views; they are built from the input's own block-mean pyramid, tiled on a 256 px lattice, Hann-blended. 256 is the model's contract โ€” it was trained and validated at this window; there is no tile-size knob on purpose.
  • No-evidence shield (on by default): black voids and blown margins are detected, filled from nearest valid content for the model's eyes only, and returned verbatim in the output. Nothing is invented outside coverage. Off-white scan paper and mounts are in-range and count as content โ€” crop scans to the photograph, or mask processing to the image face.
  • Throughput: ~0.4 MPix/s on Apple-silicon MPS (a 1024ยฒ frame in ~3 s); CUDA is faster, CPU workable for single frames. Memory is bounded by the fixed tile โ€” arbitrarily large frames stream through.
  • Determinism: same input, same output; the shipped tests.py (pytest) pins the geometry, the shield, and the small-input path.

Two checkpoints

file sha256 profile (structure/grain/tone) character
model.safetensors 9dfdd1a1โ€ฆ 1.02 / 1.07 / 1.01 on unseen pairs the model โ€” at truth on every axis
model-prof.safetensors 11cac467โ€ฆ 0.97 / 1.03 / 1.08 on unseen pairs gentler variant: slightly softer, lowest saturation โ€” prefer for very bright or fragile material

Intended use and honest limitations

  • It restores; it does not invent. Saturated regions stay saturated โ€” the training deliberately under-represents saturation so the model never learns to hallucinate content into no-evidence areas.
  • Trained on panchromatic survey film (France 1947โ€“65 ground truth; Swiss 1946 and modern Polish orthophoto content; degradations calibrated against a 1935โ€“1994 municipal archive). Colour imagery: restore luminance, carry chroma separately.
  • Below ~0.8 m/px no film ground truth exists anywhere; behaviour there is extrapolation and should be judged by eye.
  • Scan mounts and paper margins that are not saturated white count as content โ€” the no-data shield catches voids and blown regions, not off-white paper (median ~247 in the prints we examined).
  • A single tonal step wider than the model's context (~4ร— the tile, ~1 km at 1 m/px) crossing textureless ground โ€” open water, blank fields โ€” may survive partially; ordinary plate patchwork resolves fully, and through textured ground even large seams dissolve (see the Bourges panel).
  • Outputs are derivatives of the input imagery: the source's terms and credits apply to them unchanged, and no rights are claimed in restored pixels. If you serve restored imagery, label it as machine-restored and keep provenance (model, weights hash, date) โ€” the transparency posture this model ships under (cf. EU AI Act art. 50).

Training data and attribution

source role licence
IGN France โ€” raw scans + BD ORTHOยฎ Historique 1950โ€“1965 the paired ground truth Licence Ouverte / Etalab 2.0
swisstopo โ€” SWISSIMAGE HIST 1946 fine-scale film content swisstopo open data
GUGiK (Poland) โ€” Warsaw orthophoto 2025 content at working scales Polish geodetic open data, art. 40a(2)

Architecture: NAFRestore, this project's own implementation in the NAFNet family โ€” 17.32M parameters, five-channel multi-view input, trained from scratch (no pretrained initialization; there is no base model). Architecture reference: Chen, Chu, Zhang, Sun, Simple Baselines for Image Restoration, ECCV 2022 (arXiv:2204.04676; megvii-research/NAFNet, MIT).

Licence

  • Weights: CC-BY-4.0. Use them for anything, including commercially; credit "Aerial Film Restorer โ€” the Powidok project (powidok.waw.pl), CC-BY-4.0" and indicate modifications (e.g. fine-tuning).
  • Code (model.py, inference.py, tests.py): MIT (LICENSE-CODE).

What these panels show, exactly

Every output here is the bare model via the shipped inference.py โ€” no contrast grading, no sharpening. Faithful restoration is deliberately conservative: 1.00 means the negative, not a poster. In our own deployment a solved per-layer contrast normalization and a labelled presentation-sharpening pass ride on top of the model; anything similar is a downstream choice this release leaves to you.

Provenance

The model was developed for Powidok, an interactive historical map of Warsaw 1935โ€“1951 โ€” currently under construction โ€” where it restores a fourteen-layer aerial fleet; the measurement methodology (reserved-pair evaluation with standard errors, selection-bias checks, deployment gauntlets) comes from that project's training record.

Citation

Grzybek, J. (2026). Aerial Film Restorer: measured-fidelity restoration of
historical aerial survey film. The Powidok project, https://powidok.waw.pl
(model: https://huggingface.co/jan-grzybek/aerial-film-restorer)
Downloads last month
9
Safetensors
Model size
17.3M params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Paper for jan-grzybek/aerial-film-restorer