Kembali ke blog
20 Agustus 2026Sergei Solod8 mnt baca

SSIMULACRA2 untuk Transcoding WebP ke AVIF: Mengapa Saya Memakai 60 untuk Sumber dan 65 untuk Derivatif Lossy

Saat AVIF dibuat dari WebP yang sudah dikompresi secara lossy, SSIMULACRA2 hanya mengukur generasi kehilangan kedua. Karena itu saya memakai 60/58 untuk sumber bersih dan 65/63 untuk derivatif lossy yang diketahui.

AVIFWebPSSIMULACRA2Kompresi GambarPerforma Web

Aturan kompresi saya awalnya terlihat sangat sederhana: encode AVIF pada quality serendah mungkin yang masih lulus target SSIMULACRA2 60. Untuk kumpulan gambar besar, saya mengizinkan satu sample representatif turun hingga 58, sedangkan sample lain harus tetap 60 atau lebih.

Untuk PNG dan source image biasa, aturan itu terasa tepat. Masalah muncul ketika saya mulai menerima file WebP yang sebelumnya sudah dikompresi secara lossy dari original berkualitas lebih tinggi.

original berkualitas lebih tinggi: ~2 MB
          ↓
      WebP lossy: ~100 KB
          ↓
          AVIF

Apakah konversi kedua tetap boleh lulus dengan skor 60 terhadap WebP? Awalnya saya mengira iya. Skor 60 tetap 60. Namun yang berubah sebenarnya adalah gambar referensinya.

Metriknya tidak salah. Referensinya yang berubah.

SSIMULACRA2 membandingkan gambar referensi dengan gambar terdistorsi dan memberi skor pada perbedaan perseptual di antara dua input tersebut. Metrik ini dirancang untuk bereaksi terhadap kerusakan kompresi seperti blur, ringing, dan edge tambahan; materi evaluasinya juga mencakup distorsi JPEG, WebP, AVIF, dan codec lain. Dokumentasi](https://github.com/cloudinary/ssimulacra2/blob/main/README.md%22>Dokumentasi) SSIMULACRA2 menjelaskan metrik dan anchor kualitasnya secara perkiraan.

Jika saya meng-encode AVIF langsung dari source yang bagus, perbandingannya pada dasarnya source → AVIF. Skor 60 menggambarkan kerusakan yang ditambahkan oleh konversi itu.

Dengan WebP yang sudah lossy, sejarah sebenarnya berbeda:

original
   ↓ first lossy encode
WebP
   ↓ second lossy encode
AVIF

SSIMULACRA2 hanya melihat WebP → AVIF. Ia tidak mengetahui original yang ada sebelum WebP. Artifact yang sudah ada di WebP menjadi bagian dari referensi.

Jadi skor 60 dapat memberi tahu saya bahwa AVIF tidak terlalu jauh dari WebP. Skor itu tidak dapat memberi tahu seberapa jauh AVIF final dari master yang sudah hilang.

Lossy transcoding membuat quality budget kedua

Bayangkan original memiliki gradient yang bersih. Encoder pertama menambahkan sedikit banding, tetapi WebP masih terlihat baik. Lalu saya encode WebP itu ke AVIF. SSIMULACRA2 dapat menghukum degradation tambahan dari encoder AVIF, tetapi tidak dapat menghukum damage yang sudah ada di referensi.

Itulah mengapa encoding dari gambar yang sudah lossy tidak setara dengan encoding langsung dari source terbaik. Sebuah diskusi di proyek](https://github.com/AOMediaCodec/libavif/discussions/2640%22>proyek) libavif membahas prinsip yang sama: compression artifact yang sudah ada dapat terbawa ke AVIF baru ketika input sebelumnya sudah dikompresi.

Ini tidak berarti AVIF otomatis memperparah semua artifact WebP, dan tidak berarti transcoding selalu salah. Artinya encoder kedua mulai bekerja setelah sebagian quality budget original sudah terpakai.

Policy yang akhirnya saya pakai

  • Canonical atau high-quality source: target 60, floor satu sample 58.
  • Lossless WebP: target 60, floor 58.
  • Known lossy derivative: target 65, floor 63.

Perbedaan penting bukan JPEG versus WebP, melainkan canonical source versus known lossy derivative.

WebP bisa lossless. Spesifikasi](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification%22>Spesifikasi) WebP lossless menjelaskan mode yang merekonstruksi nilai pixel secara tepat, sehingga tidak ada generasi lossy sebelumnya. Sebaliknya, JPEG yang saya tahu sudah melalui beberapa transformasi lossy layak diperlakukan dengan kehati-hatian yang sama seperti WebP yang sebelumnya dikompresi.

Mengapa 65?

Tidak ada aturan SSIMULACRA2 yang mengatakan generasi lossy kedua membutuhkan tepat lima poin tambahan. Saya tidak menemukan aturan seperti itu karena memang tidak ada. Angka 65 adalah engineering policy, bukan sifat bawaan metrik.

Anchor kualitas yang dipublikasikan memberi konteks. Secara kasar, 50 berada di sekitar kualitas medium atau fair, sedangkan 70 berada di sekitar kualitas high atau good. Jadi 60 masih termasuk wilayah kompresi web yang cukup agresif, bukan visually lossless.

Untuk konversi langsung dari source bagus, saya menerima perceptual budget itu demi file yang lebih kecil. Untuk generasi lossy kedua, saya ingin budget distortion tambahan yang lebih kecil.

Saya mempertimbangkan 70, tetapi angka itu akan mendorong semua gambar hasil transcode ke wilayah kualitas yang jauh lebih ketat. Pada halaman yang memuat banyak gambar, terutama lewat koneksi mobile, byte tambahan tetap penting. Saya tidak memiliki bukti bahwa memaksa semua gambar yang sudah dikompresi mencapai 70 layak dengan biayanya. Karena itu saya memilih 65 sebagai jalan tengah yang konservatif.

Mengapa 65/63, bukan 65/62?

Policy awal saya adalah 60/58: satu representative outlier boleh dua poin di bawah target utama. Saat target utama dinaikkan menjadi 65, mempertahankan policy yang sama secara alami menghasilkan 65/63.

60 - 58 = 2
65 - 63 = 2

Memakai 62 akan menciptakan pengecualian tiga poin. Sample normal menjadi lebih ketat, sementara satu sample terburuk justru mendapat toleransi lebih besar. Saya tidak menemukan alasan teknis untuk memperlebar pengecualian khusus pada input yang sudah melewati kompresi lossy.

Baik 63 maupun 65 bukan angka ajaib. Yang penting adalah policy tetap konsisten.

Dari 2 MB menjadi 100 KB tidak menjelaskan kualitas visual

Saya sengaja tidak menurunkan threshold SSIMULACRA2 dari compression ratio. Turun dari 2 MB menjadi 100 KB terdengar dramatis, tetapi ukuran file saja hanya memberi sedikit informasi tentang perceptual degradation.

Resolusi, entropy gambar, noise, area datar, line art, chroma subsampling, dan format sebelumnya sangat memengaruhi efisiensi kompresi. Studi](https://developers.google.com/speed/webp/docs/webp_study%22>Studi) kompresi WebP Google membandingkan codec pada kualitas yang kurang lebih setara, bukan menganggap ukuran file yang sama berarti kualitas visual yang sama.

Karena itu saya tidak memakai aturan seperti 20× lebih kecil → target 65. Yang penting bagi policy saya adalah apakah file saat ini merupakan known lossy derivative, bukan seberapa mengesankan rasio pengurangannya.

Jika original masih ada, saya tidak men-transcode WebP

Jika original berkualitas tinggi dan WebP lossy kecil sama-sama masih tersedia, saya encode AVIF langsung dari original dan menggunakan policy normal 60/58.

preferred:
original → AVIF

avoid when possible:
original → lossy WebP → AVIF

Target generasi kedua yang lebih ketat tidak dapat memulihkan informasi yang hilang pada encode pertama. 65 hanya membuat AVIF tetap lebih dekat ke WebP; 70 akan membuatnya lebih dekat lagi. Keduanya tidak dapat membangun kembali original yang hilang.

Bug implementasi lebih penting daripada 62 versus 63

Saat meninjau policy ini, saya menemukan masalah yang lebih berbahaya dalam logika encoder. Code sudah memiliki format-specific target dan helper yang mampu mengembalikan target berbeda untuk WebP. Jadi mengubah WebP dari 60 menjadi 65 terlihat sederhana.

Ternyata tidak. Adaptive quality decision masih menggunakan global target dan global worst-score threshold. Format-specific target dipakai setelahnya untuk memberi label pass atau below-target pada tiap sample, tetapi belum tentu mengontrol keputusan yang memilih quality AVIF final.

Ini menciptakan failure yang halus: sample WebP bisa benar-benar diberi label di bawah target 65, tetapi adaptive search tetap menerima quality tersebut karena global pass condition masih 60.

intended WebP target: 65
actual score:         61.2

format-aware label:  below target
global search rule:  pass if target is still 60

Threshold tidak berarti jika tidak ikut menentukan keputusan yang memilih output hasil encode.

Desain yang lebih aman adalah menjadikan threshold bagian dari sample policy

Sekarang saya lebih suka memperlakukan threshold sebagai properti source, bukan sekadar format constant. Dalam pseudocode sederhana:

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

Hal terpenting adalah threshold yang dipakai untuk mendeskripsikan hasil juga menjadi threshold yang menentukan apakah hasil itu diterima.

Sampling policy juga penting

Saya tidak perlu menguji setiap gambar pada setiap candidate AVIF quality. Pipeline memilih hingga sepuluh sample JPEG, PNG, atau WebP representatif di sepanjang distribusi bytes-per-pixel.

Untuk koleksi berisi sepuluh gambar atau kurang, semua sample harus mencapai target normal. Untuk koleksi lebih besar, satu sample boleh memakai floor lebih rendah, sementara sisanya tetap harus memenuhi target utama.

Sampling membuat pencarian praktis, tetapi juga menjadi alasan agar outlier rule tidak dibuat terlalu longgar. Sample yang dipilih memang representatif, tetapi tidak membuktikan semua gambar yang tidak disampling akan berperilaku sama.

Eksperimen yang dapat menggantikan heuristic

Jawaban terkuat adalah menyimpan original sebenarnya untuk corpus representatif lalu menguji rantai lengkap:

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

Untuk setiap variant, saya akan mencatat final byte size, SSIMULACRA2 terhadap true original, SSIMULACRA2 terhadap WebP intermediate, dan encoder quality yang dipilih. Gambar yang sulit juga perlu diperiksa secara manual.

Saya belum menjalankan controlled experiment itu pada kumpulan preserved originals yang cukup representatif, sehingga saya tidak bisa mengklaim bahwa 65 adalah nilai optimal secara global. Batasan ini penting.

AVIF tidak otomatis layak untuk satu encode tambahan

Jika satu-satunya source yang tersisa adalah WebP 100 KB dan AVIF yang lolos 65/63 berukuran 96 KB, saya akan mempertanyakan konversinya. Menghemat 4 KB mungkin tidak sebanding dengan satu generasi lossy tambahan dan kompleksitas pemrosesan.

Namun jika WebP 100 KB yang sama menjadi AVIF 65 KB sambil tetap lolos quality policy, trade-off jauh lebih menarik pada halaman yang berat dengan gambar.

Konversi codec harus menjawab dua pertanyaan terpisah: apakah distortion tambahan dapat diterima, dan apakah pengurangan ukuran cukup besar untuk berarti? Lolos yang pertama tidak otomatis berarti yang kedua juga terpenuhi.

Rule yang saya gunakan sekarang

Jika saya memiliki original dengan kualitas terbaik, saya encode langsung darinya dan memakai 60/58 untuk workload web ini. Jika WebP lossless, saya juga memakai 60/58. Jika satu-satunya file yang tersisa adalah known lossy derivative, saya memakai second-generation budget yang lebih ketat, saat ini 65/63. Jika AVIF hanya sedikit mengurangi ukuran, saya mempertimbangkan mempertahankan WebP yang ada.

Pelajaran yang lebih dalam bukan bahwa WebP membutuhkan angka SSIMULACRA2 khusus. Full-reference quality metric hanya menjawab pertanyaan yang direpresentasikan oleh gambar referensinya.

Jika reference sudah kehilangan informasi, score tinggi berarti “dekat dengan reference ini”, bukan “dekat dengan image yang ada sebelumnya”. Setelah saya menjadikan provenance gambar sebagai bagian dari compression policy, threshold tidak lagi terlihat seperti codec setting yang arbitrer. Threshold menjadi budget untuk generasi kehilangan yang berbeda.