Files
noteman-slicer/docs/adr/0003-lossless-webp-with-levels-and-alpha-quantisation.md
Esa Kataja 46ae7e813a 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).
2026-07-28 22:05:09 +03:00

2.6 KiB
Raw Permalink Blame History

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 130133%
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 23 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.