Meine Kompressionsregel war zunächst angenehm einfach: AVIF mit der niedrigsten Qualität erzeugen, die noch ein SSIMULACRA2-Ziel von 60 erreicht. Bei größeren Bildmengen durfte ein repräsentatives Sample bis auf 58 fallen, während alle übrigen Samples mindestens 60 erreichen mussten.
Für PNG und normale Quelldateien funktionierte diese Regel für mich gut. Dann kamen WebP-Dateien hinzu, die bereits aus höherwertigen Originalen verlustbehaftet komprimiert worden waren, bevor ich sie bekam.
höherwertiges Original: ~2 MB
↓
Lossy-WebP: ~100 KB
↓
AVIF
Sollte auch diese zweite Konvertierung gegenüber dem WebP mit 60 bestehen dürfen? Zunächst dachte ich: ja. 60 bleibt schließlich 60. Der entscheidende Unterschied war jedoch nicht der Score, sondern das Referenzbild.
Die Metrik war nicht falsch. Die Referenz hatte sich geändert.
SSIMULACRA2 vergleicht ein Referenzbild mit einem verzerrten Bild und bewertet den wahrnehmbaren Unterschied genau zwischen diesen beiden Eingaben. Die Metrik reagiert unter anderem auf kompressionsbedingte Schäden wie Unschärfe, Ringing und künstlich erzeugte Kanten; die veröffentlichten Evaluationsdaten enthalten Verzerrungen durch JPEG, WebP, AVIF und weitere Codecs. Die SSIMULACRA2-Dokumentation](https://github.com/cloudinary/ssimulacra2/blob/main/README.md%22>SSIMULACRA2-Dokumentation) beschreibt die Metrik und ihre ungefähren Qualitätsanker.
Wenn ich AVIF direkt aus einer guten Quelle erzeuge, entspricht der Vergleich praktisch Quelle → AVIF. Ein Score von 60 beschreibt dann die durch genau diese Konvertierung eingeführte Verschlechterung.
Bei einem bereits verlustbehafteten WebP sieht die tatsächliche Historie anders aus:
Original
↓ erster Lossy-Encode
WebP
↓ zweiter Lossy-Encode
AVIF
SSIMULACRA2 sieht jedoch nur WebP → AVIF. Das ursprüngliche Bild vor dem WebP ist der Metrik unbekannt. Bereits im WebP vorhandene Artefakte werden damit Teil der Referenz.
Ein Score von 60 kann mir also sagen, dass sich das AVIF nicht zu weit vom WebP entfernt hat. Er sagt mir nicht, wie weit das endgültige AVIF vom verlorenen Master entfernt ist.
Lossy-Transcoding erzeugt ein zweites Qualitätsbudget
Angenommen, das Original enthält einen sauberen Farbverlauf. Der erste Encoder erzeugt leichtes Banding, das WebP sieht aber noch akzeptabel aus. Anschließend kodiere ich dieses WebP als AVIF. SSIMULACRA2 kann zusätzliche Verschlechterungen durch den AVIF-Encoder bestrafen, nicht aber Schäden, die bereits in der Referenz stecken.
Deshalb ist das Kodieren aus einem bereits verlustbehafteten Bild nicht dasselbe wie das direkte Kodieren aus der bestmöglichen Quelle. Eine Diskussion im libavif-Projekt](https://github.com/AOMediaCodec/libavif/discussions/2640%22>libavif-Projekt) beschreibt denselben allgemeinen Effekt: Vorhandene Kompressionsartefakte können in ein neues AVIF übernommen werden, wenn die Eingabe bereits komprimiert ist.
Das bedeutet weder, dass AVIF automatisch jedes WebP-Artefakt verstärkt, noch dass Transcoding grundsätzlich vermieden werden muss. Es bedeutet lediglich, dass der zweite Encoder startet, nachdem bereits ein Teil des ursprünglichen Qualitätsbudgets verbraucht wurde.
Die Policy, bei der ich gelandet bin
- Kanonische oder hochwertige Quelle: Ziel 60, Floor für ein einzelnes Sample 58.
- Lossless-WebP: Ziel 60, Floor 58.
- Bekanntes verlustbehaftetes Derivat: Ziel 65, Floor 63.
Die entscheidende Unterscheidung lautet nicht JPEG gegen WebP, sondern kanonische Quelle gegen bekanntes Lossy-Derivat.
WebP kann verlustfrei sein. Die WebP-Lossless-Spezifikation](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification%22>WebP-Lossless-Spezifikation) beschreibt einen Modus, der Pixelwerte exakt rekonstruiert. In diesem Fall gibt es keine vorherige verlustbehaftete Generation. Umgekehrt behandle ich ein JPEG, von dem ich weiß, dass es bereits mehrere Lossy-Transformationen durchlaufen hat, mit derselben Vorsicht wie ein zuvor komprimiertes WebP.
Warum 65?
Es gibt keine SSIMULACRA2-Regel, nach der eine zweite Lossy-Generation exakt fünf zusätzliche Punkte benötigt. Eine solche Regel habe ich nicht gefunden, weil es sie nicht gibt. 65 ist eine technische Policy, keine Eigenschaft der Metrik.
Die veröffentlichten Qualitätsanker helfen bei der Einordnung. Grob entspricht 50 mittlerer beziehungsweise akzeptabler Qualität, während 70 für hohe beziehungsweise gute Qualität steht. Damit liegt 60 eher in einem aggressiven Web-Kompressionsbereich als im visuell verlustfreien Bereich.
Bei einer direkten Konvertierung aus einer guten Quelle akzeptiere ich dieses Wahrnehmungsbudget zugunsten kleinerer Dateien. Bei einer zweiten verlustbehafteten Generation wollte ich weniger zusätzliche Verzerrung zulassen.
Ich habe 70 erwogen, doch damit würden alle transkodierten Bilder in einen deutlich strengeren Qualitätsbereich rutschen. Auf Seiten mit vielen Bildern, insbesondere über Mobilfunk, zählen die zusätzlichen Bytes. Mir fehlte die Evidenz dafür, jedes bereits komprimierte Bild zwingend auf 70 zu bringen. Deshalb habe ich 65 als konservativen Mittelweg gewählt.
Warum 65/63 und nicht 65/62?
Meine ursprüngliche Policy war 60/58. Ein repräsentativer Ausreißer durfte also zwei Punkte unter dem Hauptziel liegen. Wenn ich das Hauptziel auf 65 erhöhe und dieselbe Policy beibehalte, ergibt sich folgerichtig 65/63.
60 - 58 = 2
65 - 63 = 2
62 würde eine Ausnahme von drei Punkten erzeugen. Die normalen Samples würden strenger, während ausgerechnet das schlechteste Sample mehr Spielraum bekäme. Ich habe keinen technischen Grund gefunden, diese Ausnahme gerade bei bereits verlustbehaftet komprimierten Eingaben zu vergrößern.
Weder 63 noch 65 sind magische Zahlen. Entscheidend ist die innere Konsistenz der Policy.
2 MB auf 100 KB sagen nichts Verlässliches über die visuelle Qualität
Ich leite SSIMULACRA2-Grenzen bewusst nicht aus dem Kompressionsverhältnis ab. Eine Reduktion von 2 MB auf 100 KB klingt spektakulär, doch die Dateigröße allein sagt erstaunlich wenig über die wahrnehmbare Verschlechterung aus.
Auflösung, Bildentropie, Rauschen, gleichmäßige Flächen, Line-Art, Chroma-Subsampling und das vorherige Format beeinflussen die Kompressionseffizienz erheblich. Die WebP-Kompressionsstudie](https://developers.google.com/speed/webp/docs/webp_study%22>WebP-Kompressionsstudie) von Google vergleicht Codecs bei ungefähr angeglichener Qualität, statt gleiche Dateigröße mit gleicher visueller Qualität gleichzusetzen.
Ich verwende daher keine Regeln wie 20× kleiner → Ziel 65. Relevant ist für meine Policy, ob die aktuelle Datei ein bekanntes Lossy-Derivat ist, nicht wie beeindruckend die Größenreduktion aussieht.
Wenn das Original noch vorhanden ist, transkodiere ich das WebP nicht
Existieren sowohl das hochwertige Original als auch das kleine Lossy-WebP, kodiere ich AVIF direkt aus dem Original und verwende die normale 60/58-Policy.
bevorzugt:
Original → AVIF
wenn möglich vermeiden:
Original → Lossy-WebP → AVIF
Ein strengeres Ziel für die zweite Generation kann Informationen, die beim ersten Encode verloren gingen, nicht wiederherstellen. 65 hält das AVIF lediglich näher am WebP; 70 würde es noch näher halten. Keiner dieser Werte rekonstruiert das verlorene Original.
Der Implementierungsfehler war wichtiger als 62 gegen 63
Während der Überprüfung der Policy fand ich ein gefährlicheres Problem in der Encoder-Logik. Der Code hatte bereits formatspezifische Zielkonstanten und eine Hilfsfunktion, die für WebP ein anderes Ziel liefern konnte. Dadurch wirkte die Änderung von 60 auf 65 trivial.
Das war sie nicht. Die adaptive Qualitätsentscheidung verwendete weiterhin das globale Ziel und den globalen Worst-Score-Grenzwert. Das formatspezifische Ziel wurde später verwendet, um einzelne Ergebnisse als bestanden oder unterhalb des Ziels zu kennzeichnen, steuerte aber nicht zwingend die Auswahl der finalen AVIF-Qualität.
So entsteht ein subtiler Fehler: Ein WebP-Sample kann korrekt als unterhalb seines Zielwerts 65 markiert werden, während die adaptive Suche dieselbe Qualität akzeptiert, weil die globale Pass-Bedingung weiterhin 60 lautet.
intended WebP target: 65
actual score: 61.2
format-aware label: below target
global search rule: pass if target is still 60
Ein Grenzwert ist wertlos, wenn er nicht an der Entscheidung beteiligt ist, die das tatsächlich ausgegebene Bild auswählt.
Sicherer ist es, die Grenzwerte als Teil der Sample-Policy zu behandeln
Ich behandle die Grenzwerte inzwischen lieber als Eigenschaften der Quelle statt als dekorative Formatkonstanten. Vereinfacht:
if sample is a known lossy derivative:
target = 65
floor = 63
else:
target = 60
floor = 58
reject if any sample is below its floor
allow at most one sample below its target
Wichtig ist, dass dieselben Grenzwerte, mit denen ein Ergebnis beschrieben wird, auch darüber entscheiden, ob es akzeptiert wird.
Auch die Sampling-Policy spielt eine Rolle
Ich muss nicht jedes Bild bei jeder möglichen AVIF-Qualität testen. Die Pipeline wählt bis zu zehn repräsentative JPEG-, PNG- oder WebP-Samples über die Verteilung der Bytes pro Pixel.
Bei Sammlungen mit höchstens zehn Bildern muss jedes Sample das normale Ziel erreichen. Bei größeren Sammlungen darf ein Sample den niedrigeren Floor nutzen, während alle übrigen weiterhin das Hauptziel erfüllen müssen.
Sampling macht die Suche praktikabel. Gleichzeitig ist es ein weiterer Grund, die Ausreißerregel nicht unnötig großzügig zu machen. Die ausgewählten Samples sind repräsentativ, aber kein Beweis dafür, dass jedes nicht getestete Bild identisch reagiert.
Das Experiment, das die Heuristik ersetzen könnte
Die belastbarste Antwort ließe sich gewinnen, indem echte Originale für einen repräsentativen Korpus aufbewahrt und vollständige Ketten getestet werden:
A: original → AVIF, target 60
B: original → lossy WebP → AVIF, target 60
C: original → lossy WebP → AVIF, target 63
D: original → lossy WebP → AVIF, target 65
E: original → lossy WebP → AVIF, target 70
Für jede Variante würde ich die finale Dateigröße, SSIMULACRA2 gegen das echte Original, SSIMULACRA2 gegen das WebP-Zwischenbild und die gewählte Encoder-Qualität erfassen. Schwierige Bilder würde ich zusätzlich manuell prüfen.
Diesen kontrollierten Test habe ich noch nicht mit einem ausreichend repräsentativen Bestand erhaltener Originale durchgeführt. Deshalb kann ich nicht behaupten, dass 65 global optimal ist. Diese Einschränkung ist wichtig.
AVIF rechtfertigt nicht automatisch einen weiteren Encode
Wenn die einzige verbleibende Quelle ein 100-KB-WebP ist und das AVIF, das 65/63 besteht, 96 KB groß ist, würde ich die Konvertierung infrage stellen. Vier Kilobyte Ersparnis rechtfertigen möglicherweise keine weitere Lossy-Generation und zusätzliche Verarbeitungskomplexität.
Wird dasselbe 100-KB-WebP dagegen zu einem 65-KB-AVIF und erfüllt weiterhin die Qualitäts-Policy, ist der Trade-off auf bildreichen Seiten deutlich attraktiver.
Eine Codec-Konvertierung muss zwei getrennte Fragen beantworten: Ist die zusätzliche Verzerrung akzeptabel, und ist die Dateigrößenreduktion groß genug, um relevant zu sein? Ein Ja auf die erste Frage beantwortet die zweite nicht automatisch.
Die Regel, die ich heute verwende
Habe ich das qualitativ beste Original, kodiere ich direkt daraus und verwende für diesen Web-Workload 60/58. Ist das WebP verlustfrei, gilt ebenfalls 60/58. Ist die einzige verbleibende Datei ein bekanntes Lossy-Derivat, verwende ich ein strengeres Budget für die zweite Generation, derzeit 65/63. Reduziert AVIF die Größe kaum, behalte ich gegebenenfalls das vorhandene WebP.
Die eigentliche Erkenntnis ist nicht, dass WebP eine besondere SSIMULACRA2-Zahl benötigt. Eine Full-Reference-Metrik beantwortet nur die Frage, die ihr Referenzbild stellt.
Hat die Referenz bereits Informationen verloren, bedeutet ein hoher Score „nahe an dieser Referenz“ und nicht „nahe an dem Bild, das davor existierte“. Seit ich die Herkunft eines Bildes als Teil der Kompressions-Policy behandle, wirken die Grenzwerte nicht mehr wie willkürliche Codec-Einstellungen. Sie sind Budgets für unterschiedliche Generationen von Verlust.