Codec Roadmap#
What compressors currently supports, how each codec is plumbed in, and
what’s still on the to-do list. ✅ marks codecs with a working
evaluation harness in this repo and at least one rate-distortion +
encode-complexity sweep on Kodak under results/<codec>/.
Supported#
Conventional, via Pillow built-ins#
The compressors.pillow harness (src/compressors/pillow/) is a thin
wrapper around PIL.Image.save(format=..., quality=q). It accepts any
PIL-registered format and routes per-format kwargs through
_quality_dispatch.QUALITY_DISPATCH.
✅ JPEG — libjpeg-turbo backend.
✅ AVIF — libavif (Pillow’s built-in or
pillow-avif-plugin). Three speed settings evaluated (default,speed=0,speed=10) to characterize the full speed-quality envelope.
The harness also supports — but hasn’t been swept on Kodak yet — every other format Pillow ships, including:
⏳ WebP — libwebp (lossy + lossless).
⏳ JPEG 2000 — OpenJPEG. Note: in PIL the
quality_layerskwarg is inverted (higher value = lower output quality); already wired in_quality_dispatch.py.⏳ PNG / TIFF Adobe Deflate — lossless baselines.
Pillow plugins (third-party PIL backends)#
✅ JPEG-XL — via
pillow-jxl-plugin(libjxl). Lives insrc/compressors/jxl/, which is a thin wrapper that imports the plugin and forwards to the pillow harness with--format JXL.✅ JPEG-LS — via
pillow_jpls(CharLS). Used internally by FRAPPE, WaLLoC, and LiVeAction to entropy-code their integer latents. No standalone harness yet, but adding one is one line of glue.
Diffusers VQ autoencoders#
src/compressors/diffusers/ evaluates the four pretrained quantized
autoencoders shipped in upstream diffusers. None of these has a
native rate knob; quality is implemented as a target-pixel-ratio
resize-down inside the encoder pipeline. See
src/compressors/diffusers/_codec.py for the registry.
✅ LDM-SR (
CompVis/ldm-super-resolution-4x-openimages) —VQModel, 4× downsample, codebook 8192.✅ VQ-Diffusion ITHQ (
microsoft/vq-diffusion-ithq) —VQModel, 8× downsample, codebook 4096.✅ Kandinsky 2.1 MoVQ (
kandinsky-community/kandinsky-2-1) —VQModel, 8× downsample, codebook 16384.✅ Stable Cascade VQGAN (
stabilityai/stable-cascade) —PaellaVQModel, 4× downsample, codebook 8192.❌ Kandinsky 3 MoVQ — intentionally absent. The published
kandinsky-community/kandinsky-3movq/diffusion_pytorch_model.fp16.safetensorsships withquantize.embedding.weightall zeros; every index decodes to the zero vector and PSNR is pinned at ~12 dB. Re-add if HF fixes the upload.
Vendored learned codecs#
Upstream compressai pins legacy torch versions, so the minimal model
entropy-coding closure is vendored under
src/compressors/compressai_baselines/(BSD-3-Clause-Clear; seesrc/compressors/compressai_baselines/LICENSE). The CompressAI baselines compute bpp from forward-pass likelihoods (-log2(likelihoods).sum() / n_pixels); no_CXXrANS coder needed. MCUCoder reuses the same vendored CompressAI layers (AttentionBlock,conv,deconv) but ships its own model architecture and a per-channel min/max + Huffman entropy coder.
✅ cheng2020-anchor — Cheng, Sun, Takeuchi, Katto, CVPR 2020, GMM hyperprior without attention. 6 quality levels.
⏳ mbt2018 — Minnen, Ballé, Toderici, NeurIPS 2018, joint autoregressive + hierarchical priors. 8 quality levels. Sweep in flight (q=7,8 are slow on CPU due to the autoregressive context model).
✅ MCUCoder — Hojjat, Haberer, Landsiedel, DAGM-GCPR 2025. Asymmetric variable-rate codec with a tiny (~3-conv) encoder and a heavy
AttentionBlock-based decoder; 12 native quality levels viamodel.p = used_filter / 12. MIT, Kiel University. MS-SSIM-trained checkpoint pulled fromzenodo:14988203and cached under~/.cache/compressors/mcucoder/. fp32 PyTorch only — the upstream INT8 / TFLite / CMSIS-NN deployment path is out of scope for this baseline.
Audio#
All audio harnesses evaluate on danjacobellis/musdb_segments
(validation: 262 int16 stereo 44.1 kHz clips of 2²¹ samples) with a
shared metric stack — kbps / CR against the int16 PCM original,
waveform PSNR_dB on [0, 1]-convention normalized signals, and
SSDR / SRDR via src/compressors/spatial_audio_quality.py — an
independent PyTorch implementation of the duplex-theory decomposition
of Watcharasupat & Lerch (ICASSP 2024, arXiv 2306.08053), written
in-house because the authors’ GPL-3.0 package is unmaintained
(pins Python < 3.11) and, on inspection, solves its gain projection
against the unaligned reference, contradicting the paper’s stated
theory for delay errors. Shared sweep loops and helpers live in
src/compressors/audio_eval.py. Mono
codecs encode the two channels independently (never downmixing the
reference) and are charged for the resample to their native rate.
Lossless audio codecs (FLAC, WAV) are explicitly out of scope.
Conventional, via torchcodec (FFmpeg)#
src/compressors/torchcodec/ — real byte streams via the in-memory
AudioEncoder / AudioDecoder, --bit-rate sweep in kbps.
✅ MP3 — native 44.1 kHz stereo.
✅ Opus — encoded at 48 kHz (the only rate the encoder accepts), resampled back to 44.1 kHz on decode.
❌ Vorbis / AAC — encoders missing from torchcodec’s bundled FFmpeg.
❌ sphn — dropped; its Rust Opus encoder is hardcoded to VoIP mode with no bitrate knob.
Neural, via transformers / diffusers#
✅ EnCodec (
facebook/encodec_24khz) —src/compressors/encodec/, mono 24 kHz,--bandwidthsweep, analytic code-bit rate accounting.✅ DAC (
descript/dac_16khz) —src/compressors/dac/, mono 16 kHz,--n-quantizerssweep.✅ Mimi (
kyutai/mimi) —src/compressors/mimi/, mono 24 kHz at 12.5 frames/s,--num-quantizerssweep.✅ Stable Audio Open VAE (
stabilityai/stable-audio-open-1.0vae, diffusersAutoencoderOobleck) —src/compressors/oobleck/, native 44.1 kHz stereo, single operating point with fp16 latents (CR 64).
Deferred (audio)#
Stem-conditional evaluation (
--stemsselector over the dataset’s vocal/bass/drums/other columns).Resample-based rate sweeps for the fixed-rate codecs (the audio analog of the image pixel-ratio trick).
Spatial audio (7-ch Aria) and
facebook/encodec_48khz.Additional metrics (ViSQOL, Mel/STFT distance, SI-SDR), decode complexity, and the cross-codec audio comparison notebook (the
image_compressors.ipynbanalog).
Planned (not yet implemented)#
⏳ VVC intra (H.266) — currently the strongest conventional still- image codec. Candidate implementations:
VVenC + VVdeC (Fraunhofer HHI) — production C++; BSD-3-Clear. Likely path. Open question: Pillow plugin vs subprocess wrapper.
VTM (reference) — slow, research-only; BSD-3-Clear.
uvg266 — fast research encoder; BSD-3.
Pluggable entropy coding#
For learned codecs whose encoder/decoder are essentially lossy analysis/synthesis transforms followed by a separate entropy coding stage — currently FRAPPE, WaLLoC, and LiVeAction — the goal is to mix and match the entropy coder independently of the analysis transform. The same trained transform should be combinable with multiple entropy coders (range / arithmetic with various probability models, ANS, learned context models) so that both the rate-distortion curve and the encoder’s runtime cost can be studied in isolation.
Implications:
Treat the analysis transform and the entropy coder as separate, swappable components rather than baking them together inside a single codec class.
The entropy-coder interface should be generic enough to be reused across analysis transforms.
Evaluation (RD + complexity) should report per (transform × entropy coder) combination.
The FRAPPE harness already has a four-function pluggable contract
(compressors.frappe.entropy_coding: arrange_latents /
unarrange_latents / encode_latents / decode_latents), with
candidate alternative entropy coders living under
experiments/encoder_optimization/. Open questions: which broader
set of entropy coders to support out of the gate; whether
constriction / torchac / CompressAI’s coders can be reused as-is
or need wrapping.
Evaluation#
✅ Rate-distortion — bpp / PSNR / SSIM / LPIPS / DISTS on Kodak via
piq.SSIMLoss,piq.LPIPS,piq.DISTS. Same metric stack across every codec module above.✅ Encode-complexity — wallclock timing on 512×512 Kodak center crops, decomposed into per-stage timings (
resize/analysis/transfer/storefor codecs that internally resize-down for quality control; singleencodestage for native-rate codecs). Uses thethroughput.image.wallclocksingleton.✅ Audio rate-distortion + encode-complexity — kbps / CR / PSNR / SSDR / SRDR on full 2²¹-sample musdb clips; encode timing on 2²⁰-sample center crops of the first 24 clips with the same per-stage decomposition (
resamplefor the mono codecs, thenanalysis/transfer/store; singleencodestage for torchcodec), throughput in Msamples/s of the native 44.1 kHz crop.⏳ Static cost (FLOPs / MACs) — not implemented yet. Candidate libraries:
thop,fvcore,ptflops,calflops. Hardware- independent first-order proxy for cost.⏳ Decode-complexity — out of scope so far; deferred along with FLOP counting.
In planning#
Once enough measurements have been collected across codecs × testbeds × resolutions, package the results as a public leaderboard in the spirit of the timm leaderboard — a Hugging Face Space combining rate-distortion and computational metrics for side-by-side codec comparison. Available testbeds: Mac mini (Apple Silicon), Raspberry Pi, Intel GPU, NVIDIA GPU, various x86 CPUs.