View source for Image Quality Options

{{toc numerate=1}}
=== How comparable are these Quality option values to each other? ===

**They are not numerically comparable.** They share a 0–100 scale and a monotone direction (higher = better), but each number is interpreted by a completely different internal parameter with a different curve. ##quality 80## in JPEG and ##quality 80## in JXL are two different things that merely happen to share an integer.

What //is// comparable is the **perceptual tier** ("near-lossless", "good for web", "aggressive"), and you can build a per-format mapping for those tiers.

----

=== What each 0–100 actually maps to ===

#|
*| Format | ImageMagick maps the value onto | Underlying parameter | Direction | "Best" endpoint |*
|| **JPEG** | libjpeg quality | quantization table scale | higher = finer tables | ##100## — still **lossy** (DCT rounding, chroma subsampling) ||
|| **WebP** | libwebp quality | VBR quality target | higher = better | ##100## — near-lossless, still lossy ||
|| **AVIF** | AV1 quantizer (0–63, **inverted**) | ##qp## / quantizer | higher quality → **lower** quantizer | quantizer ##0## — mathematically lossless ||
|| **JXL** | libjxl **distance** | ##distance## | **higher quality → lower** distance | distance ##0## — mathematically lossless ||
|#

Two consequences worth internalising:
  1. **JXL and AVIF are the only two here with a true lossless mode reachable via the quality setting.** JPEG and WebP top out at "visually lossless".
  2. **JXL and AVIF are inverted internally** — a higher ImageMagick quality means a //smaller// underlying distance/quantizer. So the relationship between the number and the output is not even monotonic in the same shape across codecs.

----

=== Approximate perceptual equivalence ===

Roughly equivalent //visual// tiers for photographic content:

#|
*| Tier | JPEG | WebP | AVIF | JXL |*
|| **Near-lossless** | 90–95 | 90–95 | 70–80 | 90–100 //(distance 0–1)// ||
|| **Good / typical web** | 75–85 | 75–85 | 50–60 | 75–85 ||
|| **Aggressive / small** | 50–65 | 60–70 | 30–45 | 60–70 ||
|#

**These are estimates, not guarantees.** Treat them as starting points and validate on your own content — the exact ImageMagick↔codec mappings have shifted between releases, and content type matters enormously (a screenshot at JPEG 70 is usually fine; a night sky at JPEG 70 is not).

----

=== File size at comparable visual quality ===

For photographic content, the size ranking at equivalent perceived quality is roughly:

%%
JXL ≈ AVIF  <  WebP  <  JPEG
%%

JXL and AVIF typically land **30–50% smaller than WebP** at the same visual quality. So if you were previously using one number across all formats, the practical consequence is that **AVIF and JXL at a "moderate" setting are already better than JPEG at a "high" setting** — and smaller.

----

===Practical recommendation: presets, not shared numbers===

Rather than pretending the numbers are interchangeable, expose tiers:

%%(hl php)
/**
 * Per-format quality values that produce a comparable perceptual result.
 * Numbers are NOT interchangeable between formats — these are tuned
 * per codec so that "medium" looks the same everywhere.
 */
const QUALITY_PRESETS = [
    'max'   => ['jpeg' => 100, 'webp' => 100, 'avif' => 100, 'jxl' => 100],
    'high'   => ['jpeg' => 90, 'webp' => 88, 'avif' => 65, 'jxl' => 90],
    'medium' => ['jpeg' => 80, 'webp' => 78, 'avif' => 50, 'jxl' => 78],
    'low'    => ['jpeg' => 65, 'webp' => 62, 'avif' => 35, 'jxl' => 62],
];
%%

Usage:

%%(hl php)
$preset = \PHPThumb\Imagick::QUALITY_PRESETS['medium'];

$thumb = new \PHPThumb\Imagick('photo.jpg', [
    'jpegQuality' => $preset['jpeg'],
    'webpQuality' => $preset['webp'],
    'avifQuality' => $preset['avif'],
    'jxlQuality'  => $preset['jxl'],
]);
%%

This is exactly the argument for a **separate ##jxlQuality## option** rather than reusing ##jpegQuality##: the numbers differ, so a shared knob would systematically mis-set three of the four formats.

----

=== Measuring it yourself ===

Don't trust the table above — measure on your build and your content. Use a **lossless PNG as the reference**, not the source JPEG, otherwise you measure the source JPEG's own artifacts as if they were the new codec's.

%%(hl bash)
#!/usr/bin/env bash
SRC=reference.png

for fmt in jpg webp avif jxl; do
    echo "=== $fmt ==="
    for q in 40 50 60 70 80 90 100; do
        magick "$SRC" -quality "$q" "/tmp/t.$fmt" 2>/dev/null || continue
        size=$(stat -c%s "/tmp/t.$fmt")
        ssim=$(magick compare -metric SSIM "$SRC" "/tmp/t.$fmt" null: 2>&1 | awk '{print $1}')
        printf "  q=%-4s %9d bytes   SSIM=%s\n" "$q" "$size" "$ssim"
    done
done
%%

Then pick, for each format, the lowest quality whose SSIM still clears your threshold (e.g. ##0.98##). Those are your real per-format constants.

==== Don't forget the effort/speed defines ====

For the two modern codecs, encoder //effort// changes file size substantially at identical quality:

%%(hl bash)
# AVIF — slower = smaller, same visual quality
magick photo.jpg -quality 50 -define heic:speed=0 out.avif

# JXL — higher effort = smaller
magick photo.jpg -quality 90 -define jxl:effort=9 out.jxl
%%

So a tuned AVIF at ##speed=0## can undercut an untuned one at ##speed=5## at the //same// quality number — another reason quality numbers alone aren't a reliable cross-format comparison.

----

=== Caveats ===
  - **Chroma subsampling** affects JPEG/WebP heavily at lower qualities and is invisible to the quality number itself.
  - **WebP alpha quality** is a separate knob from the main image quality; the ##quality## option here does not control it.
  - **Graphics/flat colour** behave very differently from photos — AVIF/JXL handle flat areas poorly relative to their reputation, and PNG/WebP lossless may win outright.
  - **The exact ImageMagick mapping is version-dependent.** Treat any table (including this one) as a starting point, and verify with the script above if file size or fidelity matters.

----

=== TL;DR ===

#|
*| Question | Answer |*
|| Are the numbers interchangeable? | **No** — different internal parameters, different curves, two of them inverted ||
|| Are the //perceptual tiers// comparable? | **Yes**, with a per-format mapping table ||
|| Which formats can be truly lossless via quality? | **AVIF** and **JXL** only ||
|| Typical size ranking at equal visual quality | JXL ≈ AVIF < WebP < JPEG ||
|| Does this justify a separate ##jxlQuality##? | **Yes** — sharing ##jpegQuality## would mis-set JXL (and the others) ||
|#