imazen / imazen/zencodec

Quality calibration has no authority: reserved slot here, dead tables in zencodecs, 6 divergent per-codec overrides

Open
#120 0 comments 0 reactions 1 assignee Claimed by @lilith View on GitHub
Dominant language
Rust
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Two crates are blocked on opposite sides of one boundary.

## zencodec wrote down the gap and reserved the slot

- `src/fidelity.rs:109-113`: *"There is deliberately **no generic `Quality`** arm: the codec-agnostic `generic_quality` scale is not yet standardized (we have no agreed cross-codec meaning for quality 75)"*
- `src/fidelity.rs:156-158`: reserves `Quality(f32)` "once `generic_quality` has an agreed meaning"
- `src/traits/encoding.rs:55-68`: `with_generic_quality` promises *"a calibrated 0.0–100.0 scale"* — and ships no calibration.

## zencodecs holds the filled-in answer, as dead code

- `zenpipe/zencodecs/src/quality_calibration.rs:59,89` — `LIBJPEG_TURBO` / `MOZJPEG_EVALCHROMA`, **81,552 measured encode+score cells**, with `q_to_ssim2` (`:119`), `ssim2_to_q` (`:126`), `q_to_bpp` (`:133`).
- Gated off: `quality_calibration.rs:46-49` is `pub(crate)` + `#![allow(dead_code)]`, commented *"not yet wired into the encode path"*.
- `zenpipe/zencodecs/src/quality.rs:308-404` — a **second** set: `JPEG_TABLE`, `WEBP_TABLE`, `AVIF_TABLE`, `JXL_DISTANCE_TABLE`, `PNG_MIN/MAX_TABLE` + `interpolate` (`:281`). Its per-codec accessors (`quality.rs:168-232`) are near-dead — callers only in tests.

## The self-inconsistency

`src/traits/encoding.rs:145-151` default `with_fidelity`:
```rust
Fidelity::Lossy(
LossyTarget::ApproxSsim2(q) | LossyTarget::ApproxZensimB(q) | LossyTarget::CodecSpecificQuality(q),
) => self.with_lossless(false).with_generic_quality(q),
```
So `Fidelity::ssim2(90.0)` → `with_generic_quality(90.0)`. It **identity-maps a perceptual score onto an encoder dial** — while the adjacent arm explicitly *refuses* to do this for butteraugli ("a butteraugli *distance* has no honest codec-agnostic → 0–100 conversion"). SSIM2 90 is no more "quality 90" than butteraugli 1.0 is "quality 1". `ssim2_to_q` is exactly the missing conversion.

**Blast radius (verified):** all six first-party codecs override `with_fidelity` (`zenjpeg/.../encode.rs:340`, `zenpng/src/codec.rs:512`, `zenwebp/src/codec.rs:496`, `zenavif/src/codec.rs:468`, `zenjxl/src/codec.rs:534`, `zengif/src/codec.rs:509`), so the default is **latent, not live**. But that is the deeper problem: calibration now lives in **six independent per-codec overrides + two unused table sets in zencodecs**, with no authority. Third-party encoders that do not override get the identity map.

## Ask

Decide which calibration is canonical and put it here — this is the reserved slots content. `f32::round()/clamp()` is **not** a no_std blocker (verified: zencodec already uses `powf`/`floor`/`round`/`abs` in non-test code at `src/gainmap.rs:256,722,733,734`; CI enforces `--no-default-features` + wasm32).

**Caveat:** `LIBJPEG_TURBO`/`MOZJPEG_EVALCHROMA` are keyed to *encoder implementations*, not formats. The `q ↔ ssim2` **relation** belongs here; the impl-keyed table selection may not.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.