ব্লগে ফিরে যান
২০ আগস্ট, ২০২৬Sergei Solod8 মিনিট পড়া

WebP থেকে AVIF ট্রান্সকোডিংয়ে SSIMULACRA2: কেন সোর্সে 60 আর lossy derivative-এ 65 ব্যবহার করি

আগেই lossy-compressed WebP থেকে AVIF তৈরি করলে SSIMULACRA2 কেবল দ্বিতীয় প্রজন্মের ক্ষয় মাপে। তাই পরিষ্কার source-এর জন্য আমি 60/58 এবং জানা lossy derivative-এর জন্য 65/63 ব্যবহার করি।

AVIFWebPSSIMULACRA2ইমেজ কমপ্রেশনওয়েব পারফরম্যান্স

আমার ইমেজ কমপ্রেশনের নিয়মটা শুরুতে বেশ সরল ছিল: যত কম AVIF quality-তে SSIMULACRA2 target 60 পাস করে, সেটাই বেছে নেওয়া। বড় ইমেজ সেটে একটি representative sample-কে 58 পর্যন্ত নামতে দিতাম, কিন্তু বাকিগুলোকে 60 বা তার ওপরে থাকতে হতো।

PNG এবং স্বাভাবিক source image-এর ক্ষেত্রে এই নিয়ম নিয়ে আমি স্বচ্ছন্দ ছিলাম। সমস্যা শুরু হলো যখন এমন WebP পেলাম যেগুলো আমার হাতে আসার আগেই আরও উচ্চমানের original থেকে lossy-compress করা হয়েছিল।

higher-quality original: ~2 MB
          ↓
      lossy WebP: ~100 KB
          ↓
          AVIF

এই দ্বিতীয় conversion-এও কি WebP-এর তুলনায় 60 যথেষ্ট হওয়া উচিত? প্রথমে আমার উত্তর ছিল হ্যাঁ। 60 তো 60-ই। পরে বুঝলাম, আসল পরিবর্তনটা score-এ নয়, reference image-এ।

Metric ভুল ছিল না; reference বদলে গিয়েছিল

SSIMULACRA2 একটি reference image-এর সঙ্গে distorted image তুলনা করে এবং ওই নির্দিষ্ট দুই input-এর perceptual difference স্কোর করে। Blur, ringing, কৃত্রিম edge-এর মতো compression damage ধরার জন্য এটি তৈরি, এবং প্রকাশিত evaluation material-এ JPEG, WebP, AVIF-সহ একাধিক codec-এর distortion রয়েছে। SSIMULACRA2](https://github.com/cloudinary/ssimulacra2/blob/main/README.md%22>SSIMULACRA2) documentation-এ metric ও আনুমানিক quality anchor ব্যাখ্যা করা হয়েছে।

ভালো source থেকে সরাসরি AVIF encode করলে তুলনাটা কার্যত source → AVIF। তখন 60 স্কোরটি ওই conversion-এ যোগ হওয়া ক্ষয়কে বর্ণনা করে।

কিন্তু আগে থেকেই lossy WebP হলে প্রকৃত ইতিহাস হলো:

original
   ↓ first lossy encode
WebP
   ↓ second lossy encode
AVIF

SSIMULACRA2 দেখে শুধু WebP → AVIF। WebP তৈরি হওয়ার আগের original সম্পর্কে তার কোনো তথ্য নেই। WebP-তে আগে থেকেই থাকা artifact-গুলো reference-এর অংশ হয়ে যায়।

তাই 60 আমাকে বলতে পারে AVIF বর্তমান WebP থেকে খুব বেশি দূরে যায়নি। কিন্তু হারিয়ে যাওয়া master-এর তুলনায় final AVIF কতটা দূরে, সেটা বলতে পারে না।

Lossy transcoding দ্বিতীয় quality budget তৈরি করে

ধরা যাক original-এ পরিষ্কার gradient ছিল। প্রথম encoder সামান্য banding যোগ করল, কিন্তু WebP এখনো গ্রহণযোগ্য দেখায়। এরপর আমি WebP-টিকে AVIF করি। SSIMULACRA2 AVIF encoder-এর যোগ করা নতুন degradation penalize করতে পারে, কিন্তু reference-এ আগে থেকেই থাকা ক্ষতি penalize করতে পারে না।

এই কারণেই already-lossy image থেকে encode করা আর best available source থেকে সরাসরি encode করা এক জিনিস নয়। libavif](https://github.com/AOMediaCodec/libavif/discussions/2640%22>libavif) project-এর একটি discussion-এও একই সাধারণ বিষয় উঠে আসে: input আগে থেকেই compressed হলে বিদ্যমান compression artifact নতুন AVIF-এ বহন হয়ে যেতে পারে।

এর মানে এই নয় যে AVIF প্রতিটি WebP artifact আরও খারাপ করে দেয়, বা transcoding কখনোই করা উচিত নয়। অর্থ হলো দ্বিতীয় encoder কাজ শুরু করছে তখন, যখন original quality budget-এর কিছু অংশ ইতিমধ্যেই খরচ হয়ে গেছে।

শেষ পর্যন্ত যে policy ব্যবহার করেছি

  • Canonical বা high-quality source: target 60, একটি sample-এর floor 58।
  • Lossless WebP: target 60, floor 58।
  • জানা lossy derivative: target 65, floor 63।

গুরুত্বপূর্ণ পার্থক্য JPEG বনাম WebP নয়; বরং canonical source বনাম known lossy derivative

WebP lossless-ও হতে পারে। WebP](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification%22>WebP) lossless specification এমন mode বর্ণনা করে যা pixel value হুবহু পুনর্গঠন করে। সেক্ষেত্রে আগের lossy generation নেই। বিপরীতে, কোনো JPEG যদি ইতিমধ্যে একাধিক lossy transformation-এর মধ্য দিয়ে গেছে বলে জানা থাকে, তবে সেটিকেও previously compressed WebP-এর মতো সতর্কতার সঙ্গে দেখা উচিত।

কেন 65?

SSIMULACRA2-তে এমন কোনো নিয়ম নেই যে দ্বিতীয় lossy generation-এর জন্য ঠিক পাঁচ পয়েন্ট বাড়াতে হবে। এমন নিয়ম আমি পাইনি, কারণ সেটি নেই। 65 হলো engineering policy, metric-এর অন্তর্নিহিত বৈশিষ্ট্য নয়।

প্রকাশিত quality anchor কিছু context দেয়: আনুমানিকভাবে 50 medium বা fair quality, আর 70 high বা good quality-এর কাছাকাছি। ফলে 60 visually-lossless অঞ্চলে নয়; বরং তুলনামূলক aggressive web-compression range-এ পড়ে।

ভালো source থেকে direct conversion-এ ছোট file-এর বিনিময়ে এই perceptual budget আমি মেনে নিই। দ্বিতীয় lossy generation-এ আমি অতিরিক্ত distortion-এর budget একটু কম রাখতে চেয়েছি।

70 বিবেচনা করেছিলাম, কিন্তু তাতে সব transcoded image অনেক কঠোর quality অঞ্চলে চলে যায়। এক পেজে অনেক image load হলে, বিশেষ করে mobile connection-এ, extra byte গুরুত্বপূর্ণ। সব already-compressed image-কে 70 করতে হবে—এমন প্রমাণ আমার ছিল না। তাই conservative middle ground হিসেবে 65 বেছে নিয়েছি।

65/62 নয়, 65/63 কেন?

আমার পুরোনো policy ছিল 60/58: একটি representative outlier মূল target-এর দুই পয়েন্ট নিচে যেতে পারে। target 65 করার পর একই policy বজায় রাখলে স্বাভাবিক ফল 65/63

60 - 58 = 2
65 - 63 = 2

62 ব্যবহার করলে exception তিন পয়েন্ট হয়ে যায়। অর্থাৎ সাধারণ sample-গুলো কঠোর হবে, কিন্তু সবচেয়ে খারাপ একটিকে উল্টো আরও বেশি ছাড় দেওয়া হবে। আগে থেকেই lossy-compressed input-এর জন্য এই exception বড় করার কোনো technical reason আমি পাইনি।

63 বা 65—কোনোটাই magic number নয়। আসল লাভ হলো policy-টা internally consistent থাকে।

2 MB থেকে 100 KB হওয়া visual quality বলে না

আমি ইচ্ছাকৃতভাবে compression ratio দেখে SSIMULACRA2 threshold ঠিক করি না। 2 MB থেকে 100 KB হওয়া নাটকীয় শোনায়, কিন্তু file size একাই perceptual degradation সম্পর্কে খুব কম তথ্য দেয়।

Resolution, image entropy, noise, flat area, line art, chroma subsampling এবং আগের format—সবই compression efficiency বদলে দেয়। Google](https://developers.google.com/speed/webp/docs/webp_study%22>Google) WebP compression study সমান file size-কে সমান visual quality ধরে না; বরং আনুমানিকভাবে matched quality-তে codec তুলনা করে।

তাই আমি 20× smaller → target 65 ধরনের rule ব্যবহার করি না। আমার policy-তে গুরুত্বপূর্ণ হলো বর্তমান file known lossy derivative কি না, compression ratio কতটা চমকপ্রদ দেখাচ্ছে তা নয়।

Original থাকলে WebP থেকে transcode করি না

যদি high-quality original এবং ছোট lossy WebP দুটোই থাকে, আমি original থেকেই AVIF encode করি এবং সাধারণ 60/58 policy ব্যবহার করি।

preferred:
original → AVIF

avoid when possible:
original → lossy WebP → AVIF

দ্বিতীয় generation-এর target কঠোর করলে প্রথম encode-এ হারানো তথ্য ফেরত আসে না। 65 AVIF-কে WebP-এর কাছাকাছি রাখে; 70 আরও কাছাকাছি রাখবে। কোনোটিই হারানো original পুনর্গঠন করে না।

62 বনাম 63-এর চেয়ে implementation bug বেশি গুরুত্বপূর্ণ ছিল

Policy review করতে গিয়ে encoder logic-এ আরও বিপজ্জনক সমস্যা পাই। Code-এ format-specific target constant এবং WebP-এর জন্য আলাদা target ফেরানোর helper আগেই ছিল। তাই WebP target 60 থেকে 65 করা সহজ মনে হয়েছিল।

আসলে adaptive quality decision এখনো global target এবং global worst-score threshold ব্যবহার করছিল। Format-specific target পরে individual result-কে pass বা below-target label দিতে ব্যবহার হচ্ছিল, কিন্তু final AVIF quality বেছে নেওয়ার decision-এ সেটি বাধ্যতামূলকভাবে ব্যবহৃত হচ্ছিল না।

ফলে subtle failure হতে পারে: WebP sample-কে সঠিকভাবে 65-এর নিচে বলা হচ্ছে, কিন্তু adaptive search একই quality accept করছে কারণ global pass condition এখনো 60।

intended WebP target: 65
actual score:         61.2

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

যে threshold encoded output বাছাইয়ের decision-এ অংশ নেয় না, সেটি কার্যত অর্থহীন।

Threshold-কে sample policy-এর অংশ করা নিরাপদ

এখন আমি threshold-কে format label-এর সাজসজ্জা হিসেবে নয়, source-এর property হিসেবে ভাবতে পছন্দ করি। সরল pseudocode:

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

মূল বিষয় হলো যে threshold দিয়ে result বর্ণনা করা হয়, সেই একই threshold acceptance decision-ও নিয়ন্ত্রণ করবে।

Sampling policy-ও গুরুত্বপূর্ণ

প্রতিটি candidate AVIF quality-তে সব image test করা দরকার নেই। Pipeline bytes-per-pixel distribution জুড়ে সর্বোচ্চ দশটি representative JPEG, PNG বা WebP sample বেছে নেয়।

দশটি বা কম image থাকলে প্রতিটি sample-কে normal target পাস করতে হয়। বড় collection-এ একটি sample নিচের floor ব্যবহার করতে পারে, কিন্তু বাকিগুলোকে main target পূরণ করতে হয়।

Sampling search-কে practical করে, তবে এটাও বোঝায় যে outlier rule অতিরিক্ত ঢিলা করা ঠিক নয়। নির্বাচিত sample representative, কিন্তু unsampled প্রতিটি image একইভাবে আচরণ করবে—তার প্রমাণ নয়।

যে experiment heuristic-টাকে প্রতিস্থাপন করতে পারে

সবচেয়ে শক্ত উত্তর পেতে representative corpus-এর true original রেখে সম্পূর্ণ chain test করা দরকার:

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

প্রতিটি variant-এর জন্য final byte size, true original-এর বিরুদ্ধে SSIMULACRA2, WebP intermediate-এর বিরুদ্ধে SSIMULACRA2 এবং selected encoder quality রেকর্ড করতাম। কঠিন image হাতে দেখাও জরুরি।

যথেষ্ট representative preserved-original corpus-এ আমি এই controlled experiment করিনি। তাই 65 globally optimal—এমন দাবি করতে পারি না। এই সীমাবদ্ধতা গুরুত্বপূর্ণ।

AVIF মানেই আরেকবার encode করা সার্থক নয়

যদি একমাত্র source 100 KB WebP হয় এবং 65/63 পাস করা AVIF 96 KB হয়, আমি conversion নিয়ে প্রশ্ন তুলব। 4 KB বাঁচানোর জন্য আরেকটি lossy generation ও অতিরিক্ত processing complexity ন্যায্য নাও হতে পারে।

কিন্তু একই 100 KB WebP যদি quality policy পাস করেই 65 KB AVIF হয়, image-heavy page-এ trade-off অনেক বেশি আকর্ষণীয়।

Codec conversion-কে দুইটি আলাদা প্রশ্নের উত্তর দিতে হবে: অতিরিক্ত distortion গ্রহণযোগ্য কি না, এবং size reduction যথেষ্ট বড় কি না। প্রথমটি পাস করলেই দ্বিতীয়টি স্বয়ংক্রিয়ভাবে সত্য হয় না।

এখন যে rule ব্যবহার করি

Best-quality original থাকলে সরাসরি সেখান থেকে encode করি এবং এই web workload-এর জন্য 60/58 ব্যবহার করি। WebP lossless হলে একই 60/58। একমাত্র remaining file যদি known lossy derivative হয়, তখন stricter second-generation budget—বর্তমানে 65/63—ব্যবহার করি। AVIF যদি খুব সামান্য size কমায়, existing WebP রেখে দেওয়ার কথাও ভাবি।

গভীর শিক্ষা হলো WebP-এর জন্য কোনো বিশেষ SSIMULACRA2 number বাধ্যতামূলক নয়। Full-reference quality metric কেবল তার reference image যে প্রশ্নটি উপস্থাপন করে, সেটিরই উত্তর দেয়।

Reference যদি আগেই তথ্য হারিয়ে ফেলে, high score-এর অর্থ “এই reference-এর কাছাকাছি”, “এর আগের image-এর কাছাকাছি” নয়। Image provenance-কে compression policy-এর অংশ হিসেবে দেখা শুরু করার পর threshold-গুলো আর arbitrary codec setting মনে হয়নি; এগুলো বিভিন্ন generation of loss-এর budget হয়ে উঠেছে।