Minha regra de compressão parecia simples: gerar AVIF com a menor quality que ainda passasse em um target SSIMULACRA2 de 60. Em conjuntos maiores de imagens, eu permitia que uma única amostra representativa caísse até 58, enquanto todas as outras precisavam ficar em 60 ou mais.
Para PNG e imagens de origem normais, essa regra me parecia adequada. Depois comecei a trabalhar com arquivos WebP que já haviam sido comprimidos com perdas a partir de originais de maior qualidade antes de chegarem até mim.
original de maior qualidade: ~2 MB
↓
WebP lossy: ~100 KB
↓
AVIF
Essa segunda conversão ainda deveria poder passar com 60 em relação ao WebP? No começo, achei que sim. Um 60 continua sendo 60. O que realmente havia mudado, porém, era a imagem de referência.
A métrica não estava errada. A referência tinha mudado.
O SSIMULACRA2 compara uma imagem de referência com uma imagem distorcida e pontua a diferença perceptual entre aquelas duas entradas específicas. Ele foi projetado para reagir a danos de compressão como blur, ringing e bordas introduzidas artificialmente, e seu material de avaliação publicado inclui distorções de JPEG, WebP, AVIF e outros codecs. A documentação](https://github.com/cloudinary/ssimulacra2/blob/main/README.md%22>documentação) do SSIMULACRA2 explica a métrica e seus pontos de referência aproximados de qualidade.
Quando codifico AVIF diretamente de uma boa fonte, a comparação é essencialmente source → AVIF. Um score de 60 descreve o dano introduzido por essa conversão.
Com um WebP que já é lossy, o histórico real é diferente:
original
↓ primeiro encode com perdas
WebP
↓ segundo encode com perdas
AVIF
Mas o SSIMULACRA2 vê apenas WebP → AVIF. Ele não sabe nada sobre o original que existia antes do WebP. Os artefatos já presentes no WebP passam a fazer parte da referência.
Portanto, 60 pode dizer que o AVIF não se afastou demais do WebP. Não pode dizer o quanto o AVIF final se afastou do master perdido.
Lossy transcoding cria um segundo orçamento de qualidade
Imagine que o original tenha um gradiente limpo. O primeiro encoder introduz um pouco de banding, mas o WebP ainda parece aceitável. Depois codifico esse WebP para AVIF. O SSIMULACRA2 pode penalizar a degradação adicional introduzida pelo encoder AVIF, mas não pode penalizar o dano que já faz parte da referência.
Por isso codificar a partir de uma imagem já lossy não equivale a codificar diretamente da melhor fonte disponível. Uma discussão no projeto](https://github.com/AOMediaCodec/libavif/discussions/2640%22>projeto) libavif apresenta a mesma ideia geral: artefatos de compressão existentes podem ser carregados para um novo AVIF quando a entrada já está comprimida.
Isso não significa que AVIF amplifique automaticamente todos os artefatos de WebP, nem que transcoding nunca deva ser feito. Significa que o segundo encoder começa a trabalhar depois que uma parte do orçamento de qualidade original já foi consumida.
A política que acabei adotando
- Fonte canônica ou de alta qualidade: target 60, floor 58 para uma amostra.
- WebP lossless: target 60, floor 58.
- Derivado conhecido com perdas: target 65, floor 63.
A distinção importante não é JPEG versus WebP. É fonte canônica versus derivado lossy conhecido.
WebP pode ser lossless. A especificação](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification%22>especificação) WebP lossless descreve um modo que reconstrói exatamente os valores dos pixels; portanto, não existe uma geração lossy anterior para compensar. Da mesma forma, um JPEG que eu saiba ter passado por várias transformações com perdas merece a mesma cautela de um WebP previamente comprimido.
Por que 65?
Não existe uma regra do SSIMULACRA2 dizendo que uma segunda geração lossy exige exatamente cinco pontos adicionais. Não encontrei essa regra porque ela não existe. 65 é uma política de engenharia, não uma propriedade da métrica.
Os pontos de referência publicados ajudam a contextualizar. Aproximadamente, 50 corresponde a qualidade média ou razoável, enquanto 70 corresponde a qualidade alta ou boa. Isso coloca 60 em uma região relativamente agressiva de compressão para web, e não na região visualmente lossless.
Em uma conversão direta de uma boa fonte, aceito gastar esse orçamento perceptual em troca de arquivos menores. Para uma segunda geração com perdas, eu queria um orçamento menor para distorção adicional.
Considerei 70, mas isso levaria todas as imagens transcodificadas para uma região de qualidade consideravelmente mais rígida. Em páginas que carregam muitas imagens, especialmente em conexões móveis, bytes adicionais importam. Eu não tinha evidência de que forçar todas as imagens já comprimidas a chegar a 70 justificaria o custo. Por isso escolhi 65 como meio-termo conservador.
Por que 65/63 em vez de 65/62?
Minha política original era 60/58: uma amostra representativa podia ficar dois pontos abaixo do target principal. Ao subir o target para 65, manter a mesma política naturalmente leva a 65/63.
60 - 58 = 2
65 - 63 = 2
Usar 62 criaria uma exceção de três pontos. As amostras normais ficariam mais rígidas, enquanto justamente a pior receberia mais tolerância. Não encontrei motivo técnico para ampliar essa exceção especificamente para entradas que já passaram por compressão com perdas.
Nem 63 nem 65 são números mágicos. O importante é a consistência interna da política.
Passar de 2 MB para 100 KB não revela a qualidade visual
Eu deliberadamente não calculo thresholds SSIMULACRA2 a partir da proporção de compressão. Cair de 2 MB para 100 KB parece dramático, mas o tamanho do arquivo sozinho diz surpreendentemente pouco sobre a degradação perceptual.
Resolução, entropia da imagem, ruído, áreas planas, line art, chroma subsampling e formato anterior afetam muito a eficiência de compressão. O estudo](https://developers.google.com/speed/webp/docs/webp_study%22>estudo) de compressão WebP do Google compara codecs em níveis aproximadamente equivalentes de qualidade, em vez de assumir que tamanhos iguais significam qualidade visual igual.
Por isso não uso regras como 20× menor → target 65. O que importa para minha política é saber se o arquivo atual é um derivado lossy conhecido, não o quão impressionante parece sua redução de tamanho.
Se ainda tenho o original, não transcodifico o WebP
Se o original de alta qualidade e o pequeno WebP lossy ainda existem, codifico AVIF diretamente do original e uso a política normal 60/58.
preferido:
original → AVIF
evitar quando possível:
original → WebP lossy → AVIF
Um target mais rígido na segunda geração não consegue restaurar informações perdidas no primeiro encode. 65 apenas mantém o AVIF mais próximo do WebP; 70 o manteria ainda mais próximo. Nenhum deles recria o original perdido.
O bug de implementação era mais importante do que 62 versus 63
Ao revisar essa política, encontrei um problema mais perigoso na lógica do encoder. O código já tinha targets específicos por formato e um helper capaz de retornar um target diferente para WebP. Mudar WebP de 60 para 65 parecia trivial.
Não era. A decisão adaptativa de qualidade ainda usava o target global e o global worst-score threshold. O target específico do formato era usado depois para marcar resultados individuais como pass ou below-target, mas não necessariamente controlava a decisão que selecionava a quality AVIF final.
Isso cria um failure sutil: uma amostra WebP pode ser corretamente marcada como abaixo do seu target 65, enquanto a busca adaptativa aceita a mesma quality porque a condição global de aprovação continua sendo 60.
intended WebP target: 65
actual score: 61.2
format-aware label: below target
global search rule: pass if target is still 60
Um threshold não tem valor se não participar da decisão que escolhe o output realmente codificado.
É mais seguro tornar os thresholds parte da política de cada amostra
Hoje prefiro tratar thresholds como propriedades da fonte, não como constantes decorativas de formato. Em pseudocódigo simplificado:
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
O importante é que os mesmos thresholds usados para descrever um resultado também determinem se ele será aceito.
A política de sampling também importa
Não preciso testar cada imagem em cada candidate AVIF quality. O pipeline seleciona até dez amostras representativas de JPEG, PNG ou WebP ao longo da distribuição de bytes por pixel.
Em coleções de dez imagens ou menos, cada amostra deve atingir o target normal. Em coleções maiores, uma amostra pode usar o floor menor, enquanto todas as outras ainda precisam cumprir o target principal.
Sampling torna a busca prática, mas também é motivo para não deixar a regra de outlier permissiva demais. As amostras escolhidas são representativas; não provam que todas as imagens não amostradas se comportem de maneira idêntica.
O experimento que poderia substituir a heurística
A resposta mais forte viria de preservar os verdadeiros originais de um corpus representativo e testar cadeias completas:
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
Para cada variante eu registraria tamanho final, SSIMULACRA2 contra o verdadeiro original, SSIMULACRA2 contra o WebP intermediário e a quality escolhida pelo encoder. Também inspecionaria manualmente as imagens difíceis.
Ainda não executei esse experimento controlado em um conjunto suficientemente representativo de originais preservados, então não posso afirmar que 65 seja globalmente ótimo. Essa limitação importa.
AVIF não justifica automaticamente outro encode
Se a única fonte restante for um WebP de 100 KB e o AVIF que passa em 65/63 tiver 96 KB, eu questionaria a conversão. Economizar 4 KB pode não justificar outra geração lossy e mais complexidade de processamento.
Se o mesmo WebP de 100 KB virar um AVIF de 65 KB ainda dentro da política de qualidade, o trade-off fica muito mais interessante em páginas carregadas de imagens.
Uma conversão de codec precisa responder a duas perguntas separadas: a distorção adicional é aceitável? E a redução de tamanho é grande o bastante para importar? Passar pela primeira não garante a segunda.
A regra que uso hoje
Se tenho o original de melhor qualidade, codifico diretamente dele e uso 60/58 para esse tipo de workload web. Se o WebP for lossless, também uso 60/58. Se o único arquivo restante for um derivado lossy conhecido, aplico um orçamento mais rígido para a segunda geração, atualmente 65/63. Se AVIF reduzir pouco o tamanho, considero manter o WebP existente.
A lição mais profunda não é que WebP precise de um número especial de SSIMULACRA2. Uma full-reference quality metric só responde à pergunta representada pela sua imagem de referência.
Se a referência já perdeu informação, um score alto significa “próximo desta referência”, não “próximo da imagem que existia antes”. Quando passei a tratar a procedência da imagem como parte da política de compressão, os thresholds deixaram de parecer configurações arbitrárias do codec. Viraram orçamentos para diferentes gerações de perda.