Files
SnapOtter/docs/specs/2026-04-15-restore-photo-design.md
T
Siddharth Kumar Sah dd1e2e5ba5 docs: add restore-photo state-of-the-art redesign spec
Full pipeline redesign spec: SD-inpainting, NAFNet, CT2 colorization,
Real-ESRGAN SR, tiled inference, GPU-first, single best-quality mode.
2026-04-15 22:39:21 +08:00

208 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Restore-Photo: State-of-the-Art Redesign
**Date:** 2026-04-15
**Status:** Approved
**Scope:** Full rewrite of the restore-photo pipeline and settings UI
---
## Overview
Replace the current restore-photo pipeline with a single, always-best-quality pipeline backed by state-of-the-art open-source models running fully locally inside the Docker container. No presets, no quality tiers — the pipeline always runs at maximum quality. GPU is used when available; CPU is always a supported fallback. Users control *which* steps run via toggles, not *how well*.
---
## Models
All models are ONNX (or TFLite for BlazeFace) and downloaded at Docker build time via `hf_hub_download`. If a model file is missing at runtime, the affected step is skipped gracefully with a logged warning.
| Model | Purpose | Format | Approx. Size | HuggingFace Source |
|---|---|---|---|---|
| `lama_fp32.onnx` | Inpainting fallback (when SD model absent) | ONNX fp32 | ~200 MB | Carve/LaMa-ONNX |
| `codeformer.onnx` | Face restoration (primary) | ONNX | ~170 MB | facefusion/models-3.0.0 |
| `gfpgan_1.4.onnx` | Face restoration fallback | ONNX | ~330 MB | facefusion/models-3.0.0 |
| `nafnet_denoise.onnx` | Neural denoising | ONNX | ~68 MB | Export from megvii-research/NAFNet official weights (NAFNet-SIDD-width32) |
| `ddcolor.onnx` | Colorization (shared with colorize tool) | ONNX | ~450 MB | existing |
| `ct2_colorization.onnx` | Higher-quality colorization | ONNX | ~850 MB | Export from shuchenweng/CT2 (ICCV 2023) official weights |
| `realesrgan_x2.onnx` | Super-resolution 2× (shared with upscale tool) | ONNX | ~65 MB | existing |
| `realesrgan_x4.onnx` | Super-resolution 4× (shared with upscale tool) | ONNX | ~65 MB | existing |
| `mediapipe_blazeface.tflite` | Face detection | TFLite float16 | ~2 MB | Google MediaPipe (existing) |
| `sd_inpaint_fp16.onnx` | Diffusion inpainting (primary) | ONNX fp16 | ~1.7 GB | runwayml/stable-diffusion-inpainting quantized |
**Total new model footprint:** ~3.9 GB. Models shared with other tools are not re-downloaded.
---
## Processing Pipeline
Stages run sequentially. Each stage is a self-contained function: takes image + config, returns modified image. GPU provider priority: `CUDAExecutionProvider``CoreMLExecutionProvider``CPUExecutionProvider`.
```
Input image
[1] Pre-processing
- EXIF auto-orient
- Detect grayscale (is_grayscale flag) — mean channel diff < 5.0
- Compute adaptive tile size from image resolution
[2] Damage Detection (always runs to produce mask for step 3)
- 8-angle morphological line kernels (0°, 22.5°, 45°, 67.5°, 90°, 112.5°, 135°, 157.5°)
- Laplacian-based stain/fade/water-damage detection layer (new)
- Combines top-hat + black-hat transforms at full resolution
- Output: damage_mask at full resolution
[3] Inpainting (if scratchRemoval=true AND damage coverage ≥ 0.1%)
Primary: SD-inpainting ONNX fp16
- Overlapping tile-stitch (tile=512, stride=384)
- Poisson-blend seam correction
Fallback (if SD model absent): LaMa tiled
- tile=768, stride=512, feathered Gaussian blend
[4] Face Enhancement (if faceEnhancement=true)
- MediaPipe BlazeFace detection (unchanged)
- Skip faces < 48px
- Crop with 80% expansion for context
- CodeFormer at 512×512 (primary)
- GFPGAN at 512×512 as fallback if CodeFormer output SSIM vs. original face crop < 0.6
- Paste-back with face-aware feathered elliptical mask + color-transfer correction
[5] Denoising (if denoise=true)
- NAFNet ONNX, tiled at full resolution
- denoiseStrength (0100) maps to NAFNet sigma parameter
- Falls back to NLMeans if NAFNet model absent
[6] Super Resolution (if superResolution=true)
- Real-ESRGAN x4 if input < 2 MP
- Real-ESRGAN x2 if input ≥ 2 MP
- Tiled inference with seam blending (reuse upscale tool implementation)
[7] Colorization (if colorize=true)
- CT2 tiled (tile=512, stride=384)
- Falls back to DDColor tiled if CT2 model absent
- colorizeStrength (0100) controls LAB a/b blend intensity
- Preserves original luminance channel
Output image
```
**Progress milestones** (stderr JSON `{"progress": N, "stage": "..."}`):
| % | Stage |
|---|---|
| 5 | Pre-processing |
| 10 | Damage detection |
| 25 | Inpainting |
| 45 | Face enhancement |
| 60 | Denoising |
| 75 | Super resolution |
| 90 | Colorization |
| 95 | Saving output |
| 100 | Complete (set by API route) |
---
## API Changes
### Removed
- `mode: "auto" | "light" | "heavy"` — removed entirely
### Unchanged
- `scratchRemoval: boolean` (default `true`)
- `faceEnhancement: boolean` (default `true`)
- `fidelity: number` 01 (default `0.7`) — CodeFormer fidelity
- `denoise: boolean` (default `true`)
- `denoiseStrength: number` 0100 (default `40`)
- `colorize: boolean` (default `false`)
### Added
- `superResolution: boolean` (default `true`) — allow user to disable SR if they want original dimensions
- `colorizeStrength: number` 0100 (default `85`) — was hardcoded `0.85` internally
### Timeout
Bridge timeout raised from 300s → 1200s to accommodate SD-inpainting on CPU for large images.
---
## Frontend Changes (`restore-photo-settings.tsx`)
- Remove 3-column mode button group (`auto / light / heavy`)
- Add **Super Resolution** checkbox (default on)
- Add **Colorize Strength** slider (0100, step 5) — shown when `colorize` is on, same pattern as `fidelity` slider
- `denoiseStrength` slider remains; now controls NAFNet sigma rather than NLMeans h
No other UI changes. Settings panel stays compact.
---
## i18n Additions (`packages/shared/src/i18n/en.ts`)
```ts
superResolution: "Super Resolution",
colorizeStrength: "Colorize Strength",
```
Remove: `restoreMode`, `restoreModeAuto`, `restoreModeLight`, `restoreModeHeavy` (if present).
---
## Error Handling
| Scenario | Behavior |
|---|---|
| SD model missing | Fall back to LaMa tiled; log warning |
| CT2 model missing | Fall back to DDColor tiled; log warning |
| NAFNet model missing | Fall back to NLMeans; log warning |
| GFPGAN model missing | Skip GFPGAN fallback; CodeFormer only |
| No damage detected (< 0.1% coverage) | Skip inpainting entirely |
| Tiling dimension mismatch | Fall back to 512px single-pass for that stage; log warning |
| CUDA unavailable | Silently use CPU provider |
| SD-inpainting CPU timeout | Bridge timeout is 1200s; if exceeded, API returns 504 with clear message |
---
## Testing
### Unit (Vitest)
- API Zod schema accepts `superResolution` and `colorizeStrength`
- API schema rejects old `mode` field
- `colorizeStrength` default is 85
### Integration (Vitest)
- POST restore-photo with fixture image → assert output dimensions ≥ input (SR ran)
- POST with `superResolution: false` → assert output dimensions equal input
- POST with `colorize: true` → assert output is not identical to input
### Python (pytest)
- Each pipeline stage tested in isolation with small fixture image:
- Damage detection returns a binary mask with same H×W as input
- LaMa tiled returns same-shape image
- NAFNet tiled returns same-shape image
- Real-ESRGAN x2 returns 2× dimensions
- CT2 tiled returns same-shape image
### E2E (Playwright)
- Upload fixture old photo → verify before-after slider renders with non-blank result
- Verify Super Resolution checkbox appears in settings panel
---
## File Paths Affected
| File | Change |
|---|---|
| `packages/ai/python/restore.py` | Full rewrite of pipeline |
| `packages/ai/python/download_models.py` | Add NAFNet, CT2, SD-inpainting, GFPGAN download entries |
| `packages/ai/src/restoration.ts` | Update `RestoreOptions` interface |
| `apps/api/src/routes/tools/restore-photo.ts` | Update Zod schema, raise timeout |
| `apps/web/src/components/tools/restore-photo-settings.tsx` | Remove mode group, add SR + colorizeStrength controls |
| `packages/shared/src/i18n/en.ts` | Add/remove i18n keys |
| `packages/shared/src/constants.ts` | Update tool timeout constant if applicable |