Replace the handoff notes with a durable spec and ADRs
The handoff was written as a message to relay information; several decisions lived only there. Split into permanent homes: - docs/spec.md — scope, geometry model, pipeline order, detection, levels, editor, project file, markers, bundle format, noteman's changes, reference values - ADR 0002 — raster only in release 1; SVG slices measured at 40x WebP naive, 2.6x with a bounding-box cull, deferred on risk not size - ADR 0003 — lossless WebP with levels and 16-level alpha; every lossy option and alternative format measured larger for line art - ADR 0004 — detection proposes, the human disposes; no unattended mode - ADR 0005 — PyMuPDF for all PDF access, accepting AGPL - ADR 0006 — systems are found by vertical brackets, not row-darkness gaps, which miscount every page of a 6-voice score Also from testing against the hardest score in the repertoire: scanned PDFs carry their scan as an embedded image and must be extracted at native resolution rather than re-rendered at 600 DPI, and per-page deskew is mandatory (skew varies -2.6 to +1.2 within one PDF).
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Lossless WebP, with levels and alpha quantised to 16 levels
|
||||
|
||||
Slice images are encoded as **lossless WebP**, with the levels adjustment applied
|
||||
and the alpha channel quantised to 16 levels. About 7 KB per slice, ~450 KB for a
|
||||
65-system song. Every lossy option and every alternative format measured
|
||||
*larger* for this content, which is the opposite of the usual intuition — hence
|
||||
this record.
|
||||
|
||||
## The measurement
|
||||
|
||||
20 slices of one real song, levels applied throughout, relative to plain lossless
|
||||
WebP:
|
||||
|
||||
| | vs baseline | |
|
||||
|---|---|---|
|
||||
| **WebP lossless + alpha quantised to 16** | **68%** | chosen |
|
||||
| AVIF q60 | 90% | lossy, for 10% |
|
||||
| WebP lossless | 100% | baseline |
|
||||
| WebP lossy q85 (alpha) | 107% | |
|
||||
| AVIF q85 | 114% | |
|
||||
| JXL lossless | 130–133% | |
|
||||
| WebP lossy q85 (opaque ink-on-white) | 158% | |
|
||||
| PNG grayscale + alpha | 165% | |
|
||||
| AVIF lossless | 188% | |
|
||||
|
||||
Separately, before levels: applying levels alone takes 338 KB → 211 KB, a 38%
|
||||
reduction.
|
||||
|
||||
## Four results that contradict an instinct
|
||||
|
||||
- **Lossy is bigger than lossless here.** Not a quality problem — the measured
|
||||
difference between q85 and lossless is max 12/255, mean 0.33, i.e. invisible.
|
||||
Lossy VP8 simply spends more bits on sharp black/white edges than VP8L's
|
||||
palette and predictor transforms do, and notation is nothing but sharp edges.
|
||||
The "q85 looks fine" intuition comes from photographs and inverts here.
|
||||
- **AVIF and JXL both lose**, AVIF lossless by nearly 2×. Their lossless modes
|
||||
are afterthoughts on photo codecs. WebP's VP8L is close to purpose-built for
|
||||
flat two-tone line art — sheet music is the content type it is best at. JXL
|
||||
additionally has no path forward in Chrome.
|
||||
- **Alpha costs nothing.** Opaque ink-on-white and black-plus-alpha are within
|
||||
0.1% at lossless, so paper-tint removal and future non-rectangular slices are
|
||||
free.
|
||||
- **Levels is the single biggest lever** — 38%, as a side effect of a control
|
||||
that exists for quality reasons anyway. Pushing the white point below the
|
||||
paper's luminance sets vast regions to exactly `alpha = 0`, which costs almost
|
||||
nothing to encode.
|
||||
|
||||
Alpha quantisation to 16 levels is imperceptible: antialiased edges span 2–3 px
|
||||
at 1920, and 16 steps across that is below notice. 8 levels starts to gamble on
|
||||
thin strokes.
|
||||
|
||||
## Rejected as not worth it
|
||||
|
||||
- **Encoder effort tuning** — Pillow's `method=6` buys 3% and a dependency.
|
||||
- **`alpha_quality=60`** — 24%, for less control than quantisation gives.
|
||||
- **Grayscale WebP** — no such mode exists. It wouldn't help anyway: the RGB
|
||||
channels are constant black and compress to nearly nothing, so alpha is the
|
||||
entire payload.
|
||||
Reference in New Issue
Block a user