Un chiffre de qualité ment : ce que mesure ssimulacra2
Chaque encodeur d’image a un curseur de qualité : -q 80, -quality 90, un cq-level. On dirait une promesse : réglez-le sur 80 et chaque image ressort à « 80 % de bien ». Ce n’est pas le cas. Ce chiffre fixe un paramètre de compression, pas un résultat, et le résultat dépend entièrement de l’image.
On peut montrer précisément à quel point ce chiffre ment.
Le même réglage, une qualité radicalement différente
Nous avons encodé 1,998 photographies réelles en JPEG à -q 80 fixe (libjpeg-turbo), puis noté chacune avec ssimulacra2, une métrique perceptuelle où un chiffre plus élevé signifie plus proche de l’original.
vips -q 80 sur 1,998 photos |
ssimulacra2 |
|---|---|
| pire image | 58 |
| 10e percentile | 74 |
| médiane | 77 |
| 90e percentile | 80 |
| meilleure image | 87 |
Un seul réglage. Un écart de 29 points en qualité réelle. À -q 80, une photo ressort à 58 (visiblement dégradée, pleine de blocs et de bouillie), tandis qu’une autre passe à 87, indiscernable de la source et chargée d’octets superflus. Le chiffre sur le curseur ne disait rien de l’une ni de l’autre.
Ça empire d’un encodeur à l’autre. -q 80 en JPEG n’est pas -q 80 en WebP, qui n’est pas cq-level 30 en AVIF : chaque échelle est définie par son propre encodeur, donc les mêmes chiffres veulent dire des choses différentes. Un chiffre de qualité n’est même pas comparable à lui-même d’un outil à l’autre.
Pourquoi le contenu d’une photo décide de sa qualité
Un réglage de qualité détermine l’agressivité avec laquelle on jette du détail. Un ciel uni ou un portrait doux a peu de détail à perdre, donc un réglage agressif fait peu de mal : le score est élevé. Une photo de feuillage, de gravier ou de tissu n’est que détail fin, donc le même réglage la déchiquette : le score est bas. L’encodeur a appliqué la règle identique ; les images avaient des quantités de détail à perdre radicalement différentes.
C’est pourquoi « prenez juste la qualité 80 » vous livre discrètement deux échecs à la fois : des images dégradées au point qu’un utilisateur le remarque, et des images 30% plus lourdes que nécessaire. Ça ne se voit pas dans les réglages, seulement dans le résultat.
Ce qu’il faut viser à la place
Visez le résultat, pas le paramètre. ssimulacra2 est construit pour ça : il est calibré en unités de différence perçue (à peu près des « différences tout juste perceptibles » par rapport à l’original), donc un score cible signifie la même chose sur chaque image et dans chaque format.
- ~90 : indiscernable de l’original à un affichage normal.
- ~85 : qualité élevée ; il faut chercher la différence.
- ~80 : bon ; suffisant pour la plupart des images web.
- en dessous de ~70 : la différence se voit.
Choisissez une cible, puis laissez la qualité de l’encodeur être cherchée image par image pour l’atteindre. La photo de feuillage reçoit un réglage plus élevé parce qu’elle en a besoin ; le ciel uni en reçoit un plus bas parce qu’il le peut. Chaque image atteint la même qualité perçue, chacune avec le minimum d’octets pour y parvenir. Aucune image n’est discrètement ruinée, aucune n’est discrètement gonflée.
C’est ce que fait Patu sur chaque image : une recherche de qualité d’encodeur propre à chaque image pour atteindre une cible ssimulacra2, plutôt qu’un -q fixe qui veut dire autre chose sur chaque photo.
curl -X POST "https://patu.dev/v1/compress?target=85" \
-H "X-Api-Key: $PATU_KEY" \
--data-binary @photo.jpg -o photo.avif
La métrique fait le travail qu’un chiffre de qualité prétend faire : décrire l’image que vous obtenez réellement.
Méthode et données : le harnais de benchmark Patu sur la catégorie photo (1,998 photographies réelles) ; libjpeg-turbo via vips ; notées avec ssimulacra2 via l’implémentation rust-av. Les percentiles portent sur toutes les images notées au réglage fixe.