AraMS-Restore β restoration generators
Two U-Net generators that take a degraded Arabic manuscript line and return a readable one. Same architecture, same data, same schedule β they differ only in what they optimize:
unimodal (Level 1) |
recaware (Level 2) |
|
|---|---|---|
| Loss | L1 + ResNet50 perceptual | the same + a frozen HATFormer OCR critic |
| Optimizes for | visual fidelity | legibility |
| Text | not used | used as a loss target, never as an input |
The Level-2 critic reads the restored image and is scored against the true transcription, so gradients flow into the pixels that matter for reading. Code: github.com/ArchaText/AraMS-Restore.
Results
On real damaged lines from a held-out manuscript β the honest test β the image-only model does not help, while the recognition-aware one does:
| Input to the reader | CER % | WER % |
|---|---|---|
| Degraded (untouched) | 29.81 | 59.98 |
| Level 1 restored | 30.15 | 59.98 |
| Level 2 restored | 28.15 | 58.44 |
The ranking inverts between the two ways of measuring β Level 1 wins every pixel metric it is optimized for, and loses on reading:
| PSNR β | SSIM β | CER β | |
|---|---|---|---|
| Degraded | 22.76 | 0.9498 | 35.11 |
| Level 1 | 34.66 | 0.9886 | 28.12 |
| Level 2 | 30.66 | 0.9781 | 27.13 |
(Synthetic test set. CER read by the frozen HATFormer reader.)
Files
unimodal/best.pt 124 MB Level 1 generator (plain state_dict)
recaware/best.pt 124 MB Level 2 generator (plain state_dict)
Each is a state_dict for the generator built by
restoration.models.unet_pix2pix.build_generator: 31.04 M parameters, float32,
RGB in/out, base=64, depth=4, no discriminator (pix2pix off).
Usage
hf download Archatext/Restoration --local-dir checkpoints/restore
That is exactly the layout the configs resolve, so the repo's scripts work unchanged:
python scripts/restore_real.py --score # CER before vs. after, both models
python app/server.py # drag-and-drop web demo
Or load one directly:
import torch, yaml
from restoration.models.unet_pix2pix import build_generator
cfg = yaml.safe_load(open("configs/restore_recaware.yaml"))
gen = build_generator(cfg["model"])
gen.load_state_dict(torch.load("checkpoints/restore/recaware/best.pt", map_location="cpu"))
gen.eval()
Input representation
A line is resized to height 64 keeping aspect and right-padded to width 1152
(build_strip), RGB in [0, 1], padding pure black and excluded by a mask. 1152 =
3 Γ 384 tiles cleanly into the HATFormer 384 Γ 384 RTL-flipped canvas, which is
what makes the Level-2 recognition loss exact. Feeding a differently-shaped or
grayscale image will degrade output quality.
Training
Both models: 20 epochs, AdamW lr=2e-4 (Ξ²=0.5, 0.999), batch 8, seed 1337, and 4
distinct degraded variants per clean line per epoch. Damage is synthesized on the
fly from clean train-split lines only β never val/test β by the localized
degradation engine (erasure, tears, worm holes, bleed-through, ink blots, ink
feathering), calibrated against real manuscript decay.
| weights | |
|---|---|
unimodal |
L1 1.0, perceptual 0.02 |
recaware |
image 1.0 (L1 1.0 + perceptual 0.02), recognition 0.5 |
Level 2's critic is Archatext/hatformer-arams28k,
frozen throughout.
Training data: AraMS-28k-HTR,
train split β 20,103 lines from 9 manuscripts.
Leakage
Splits are manuscript-level disjoint: the evaluation manuscripts (book_03, 05, 09) are held out of restoration training entirely, and the OCR critic that scores the output never trained on them either. Both conditions are needed β a critic that had seen the test books would contaminate the training signal and the metric at once.
Limitations
- Trained on synthetic degradation; real decay is a different distribution, and the gap shows β the CER gain on real damage (1.7 points) is smaller than on the synthetic test set.
- Single script tradition and a single corpus of 14 manuscripts; no claim of generalization to other hands, papers or languages.
- Level 1 makes real damage marginally harder to read (30.15 vs 29.81 CER) while scoring far better on PSNR/SSIM β do not select a restoration model on pixel metrics alone.
- Line-level only. Page segmentation is upstream and not part of this release.