بازگشت به بلاگ
۲۹ مرداد ۱۴۰۵Sergei Solod9 دقیقه مطالعه

SSIMULACRA2 در تبدیل WebP به AVIF: چرا برای منبع 60 و برای مشتق‌های Lossy عدد 65 را استفاده می‌کنم

وقتی AVIF از WebPای ساخته می‌شود که قبلاً با اتلاف فشرده شده، SSIMULACRA2 فقط نسل دوم افت کیفیت را می‌سنجد. به همین دلیل برای منابع تمیز 60/58 و برای مشتق‌های Lossy شناخته‌شده 65/63 را انتخاب کرده‌ام.

AVIFWebPSSIMULACRA2فشرده‌سازی تصویرکارایی وب

قاعده فشرده‌سازی من در ابتدا ساده بود: AVIF را با پایین‌ترین qualityای بسازم که هنوز هدف SSIMULACRA2 برابر 60 را پاس می‌کند. در مجموعه‌های بزرگ‌تر اجازه می‌دادم یک نمونه نماینده تا 58 پایین بیاید، اما بقیه نمونه‌ها باید حداقل 60 می‌ماندند.

برای PNG و تصاویر منبع معمولی این سیاست مناسب بود. مسئله زمانی شروع شد که با فایل‌های WebPای روبه‌رو شدم که قبل از رسیدن به من، از اصل‌های باکیفیت‌تر به‌صورت lossy فشرده شده بودند.

اصل باکیفیت‌تر: ~2 MB
          ↓
      WebP با اتلاف: ~100 KB
          ↓
          AVIF

آیا تبدیل دوم هم باید در مقایسه با WebP با امتیاز 60 پذیرفته شود؟ ابتدا فکر می‌کردم بله. 60 همان 60 است. اما چیزی که تغییر کرده بود خود تصویر مرجع بود.

متریک اشتباه نبود؛ مرجع تغییر کرده بود

SSIMULACRA2 یک تصویر مرجع را با تصویر تحریف‌شده مقایسه می‌کند و اختلاف ادراکی همین دو ورودی را امتیاز می‌دهد. این متریک برای واکنش به آسیب‌های فشرده‌سازی مانند blur، ringing و لبه‌های مصنوعی طراحی شده و مواد ارزیابی منتشرشده آن شامل اعوجاج‌های JPEG، WebP، AVIF و چند کدک دیگر است. مستندات](https://github.com/cloudinary/ssimulacra2/blob/main/README.md%22>مستندات) SSIMULACRA2 خود متریک و نقاط مرجع تقریبی کیفیت را توضیح می‌دهد.

وقتی AVIF را مستقیم از یک منبع خوب می‌سازم، مقایسه عملاً source → AVIF است. امتیاز 60 در این حالت افتی را توصیف می‌کند که همان تبدیل ایجاد کرده است.

اما با WebPای که قبلاً lossy شده، تاریخچه واقعی چنین است:

original
   ↓ first lossy encode
WebP
   ↓ second lossy encode
AVIF

SSIMULACRA2 فقط WebP → AVIF را می‌بیند و چیزی از original قبل از WebP نمی‌داند. artifactهایی که از قبل در WebP وجود دارند بخشی از مرجع شده‌اند.

بنابراین امتیاز 60 می‌تواند بگوید AVIF خیلی از WebP دور نشده، اما نمی‌تواند بگوید AVIF نهایی چقدر از master از دست‌رفته فاصله دارد.

Transcoding با اتلاف یک بودجه کیفیت دوم می‌سازد

فرض کنید original یک گرادیان تمیز دارد. encoder اول کمی banding اضافه می‌کند، ولی WebP هنوز قابل قبول است. بعد همان WebP را به AVIF تبدیل می‌کنم. SSIMULACRA2 می‌تواند افت اضافه‌شده توسط AVIF encoder را جریمه کند، اما نمی‌تواند آسیبی را جریمه کند که از قبل در تصویر مرجع وجود دارد.

به همین دلیل encode از تصویری که قبلاً lossy شده با encode مستقیم از بهترین منبع موجود یکسان نیست. در یک بحث در پروژه](https://github.com/AOMediaCodec/libavif/discussions/2640%22>پروژه) libavif نیز همین نکته کلی مطرح شده است: اگر ورودی از قبل فشرده باشد، artifactهای موجود ممکن است به AVIF جدید منتقل شوند.

این به آن معنا نیست که AVIF حتماً تمام artifactهای WebP را تشدید می‌کند یا transcoding همیشه اشتباه است. فقط یعنی encoder دوم زمانی شروع به کار می‌کند که بخشی از بودجه کیفیت اصل قبلاً مصرف شده است.

سیاستی که در نهایت انتخاب کردم

  • منبع canonical یا باکیفیت: target برابر 60 و floor یک نمونه برابر 58.
  • WebP بدون اتلاف: target برابر 60 و floor برابر 58.
  • مشتق شناخته‌شده با اتلاف: target برابر 65 و floor برابر 63.

تفاوت اصلی JPEG در برابر WebP نیست؛ بلکه منبع canonical در برابر مشتق شناخته‌شده lossy است.

WebP می‌تواند lossless باشد. مشخصات](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification%22>مشخصات) WebP lossless حالتی را توضیح می‌دهد که مقادیر پیکسل را دقیق بازسازی می‌کند؛ در نتیجه نسل قبلی از افت کیفیت وجود ندارد. برعکس، JPEGای که می‌دانم چند بار lossy تبدیل شده، همان احتیاط WebP فشرده‌شده قبلی را می‌طلبد.

چرا 65؟

هیچ قاعده‌ای در SSIMULACRA2 وجود ندارد که بگوید نسل دوم lossy دقیقاً پنج امتیاز بیشتر لازم دارد. چنین قاعده‌ای پیدا نکردم، چون وجود ندارد. 65 یک سیاست مهندسی است، نه ویژگی خود متریک.

نقاط مرجع کیفیت منتشرشده برای درک جایگاه آن مفیدند: تقریباً 50 معادل کیفیت متوسط یا قابل قبول و 70 معادل کیفیت بالا یا خوب در نظر گرفته می‌شود. بنابراین 60 در محدوده نسبتاً تهاجمی فشرده‌سازی وب قرار دارد، نه در محدوده visually lossless.

برای تبدیل مستقیم از منبع خوب، مصرف این بودجه ادراکی را در ازای فایل کوچک‌تر قبول دارم. برای نسل دوم lossy می‌خواستم بودجه اعوجاج اضافه کمتر باشد.

70 را هم بررسی کردم، اما در آن صورت تمام تصاویر transcoded وارد محدوده بسیار سخت‌گیرانه‌تری می‌شدند. در صفحه‌ای با تصاویر زیاد، مخصوصاً روی اتصال موبایل، بایت‌های اضافه اهمیت دارند. مدرکی نداشتم که اجبار همه تصاویر از قبل فشرده‌شده به 70 این هزینه را توجیه کند، بنابراین 65 را به‌عنوان نقطه میانی محافظه‌کارانه انتخاب کردم.

چرا 65/63 و نه 65/62؟

سیاست اولیه من 60/58 بود؛ یعنی یک outlier نماینده می‌توانست دو امتیاز زیر target اصلی قرار بگیرد. وقتی target را به 65 افزایش دادم، حفظ همان سیاست به‌طور طبیعی 65/63 را می‌دهد.

60 - 58 = 2
65 - 63 = 2

استفاده از 62 استثنای سه‌امتیازی می‌سازد. در این حالت نمونه‌های عادی سخت‌گیرانه‌تر می‌شوند اما بدترین نمونه آزادی بیشتری می‌گیرد. دلیل فنی برای بزرگ‌تر کردن این استثنا مخصوص ورودی‌هایی که قبلاً lossy شده‌اند پیدا نکردم.

نه 63 و نه 65 عدد جادویی نیستند. ویژگی مفید این است که سیاست از نظر داخلی سازگار باقی می‌ماند.

کوچک شدن 2 MB به 100 KB کیفیت بصری را مشخص نمی‌کند

عمداً thresholdهای SSIMULACRA2 را از نسبت فشرده‌سازی استخراج نمی‌کنم. تبدیل 2 MB به 100 KB چشمگیر است، اما اندازه فایل به‌تنهایی اطلاعات کمی درباره افت ادراکی می‌دهد.

رزولوشن، entropy تصویر، نویز، نواحی یکنواخت، line art، chroma subsampling و فرمت قبلی همگی کارایی فشرده‌سازی را تغییر می‌دهند. مطالعه](https://developers.google.com/speed/webp/docs/webp_study%22>مطالعه) فشرده‌سازی WebP گوگل کدک‌ها را در کیفیت تقریباً هم‌سطح مقایسه می‌کند، نه با فرض اینکه حجم مساوی یعنی کیفیت بصری مساوی.

به همین دلیل قاعده‌هایی مانند 20× کوچک‌تر → target 65 ندارم. برای سیاست من مهم است که فایل فعلی مشتق شناخته‌شده lossy باشد یا نه، نه اینکه نسبت کاهش حجم چقدر چشمگیر به نظر برسد.

اگر original را دارم، WebP را transcode نمی‌کنم

اگر هم original باکیفیت و هم WebP کوچک lossy موجود باشد، AVIF را مستقیم از original می‌سازم و همان سیاست 60/58 را استفاده می‌کنم.

preferred:
original → AVIF

avoid when possible:
original → lossy WebP → AVIF

target سخت‌گیرانه‌تر در نسل دوم نمی‌تواند اطلاعات از دست‌رفته در encode اول را بازگرداند. 65 فقط AVIF را به WebP نزدیک‌تر نگه می‌دارد؛ 70 آن را نزدیک‌تر هم می‌کند. هیچ‌کدام original از دست‌رفته را بازسازی نمی‌کنند.

باگ پیاده‌سازی از بحث 62 و 63 مهم‌تر بود

هنگام بازبینی سیاست، مشکل خطرناک‌تری در منطق encoder پیدا کردم. کد از قبل targetهای جداگانه بر اساس format و helperی داشت که می‌توانست برای WebP target متفاوتی برگرداند. بنابراین تغییر WebP از 60 به 65 ساده به نظر می‌رسید.

اما نبود. تصمیم adaptive quality هنوز از target سراسری و worst-score سراسری استفاده می‌کرد. target مخصوص format بعداً برای برچسب‌گذاری نتیجه به‌عنوان pass یا below-target استفاده می‌شد، اما الزاماً تصمیم انتخاب quality نهایی AVIF را کنترل نمی‌کرد.

این یک failure ظریف می‌سازد: sample مربوط به WebP ممکن است درست به‌عنوان پایین‌تر از target 65 برچسب بخورد، ولی adaptive search همان quality را قبول کند چون شرط کلی pass هنوز 60 است.

intended WebP target: 65
actual score:         61.2

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

thresholdی که در تصمیم انتخاب خروجی encode‌شده شرکت نکند عملاً بی‌معناست.

راه امن‌تر این است که threshold بخشی از policy هر sample باشد

حالا ترجیح می‌دهم thresholdها را ویژگی source بدانم، نه ثابت‌های تزئینی format. در 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هایی که نتیجه را توصیف می‌کنند، قبول یا رد شدن آن را هم کنترل کنند.

Sampling policy هم مهم است

لازم نیست هر تصویر را در هر quality احتمالی AVIF تست کنم. pipeline حداکثر ده نمونه نماینده JPEG، PNG یا WebP را در طول توزیع bytes-per-pixel انتخاب می‌کند.

در مجموعه‌های ده‌تصویری یا کوچک‌تر، هر sample باید target عادی را بگیرد. در مجموعه‌های بزرگ‌تر، یک sample می‌تواند از floor پایین‌تر استفاده کند، اما بقیه باید target اصلی را پاس کنند.

Sampling جست‌وجو را عملی می‌کند، اما دلیل دیگری است که outlier rule را بی‌دلیل باز نگذارم. نمونه‌های انتخاب‌شده نماینده‌اند، اما ثابت نمی‌کنند همه تصاویر نمونه‌برداری‌نشده رفتار یکسانی دارند.

آزمایشی که می‌تواند جای heuristic را بگیرد

قوی‌ترین پاسخ با نگه داشتن originalهای واقعی برای یک corpus نماینده و تست زنجیره کامل به دست می‌آید:

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، اندازه نهایی، SSIMULACRA2 در برابر original واقعی، SSIMULACRA2 در برابر WebP میانی و quality انتخاب‌شده encoder را ثبت می‌کردم. تصاویر دشوار را هم دستی بررسی می‌کردم.

این آزمایش کنترل‌شده را روی مجموعه‌ای به‌اندازه کافی نماینده از originalهای حفظ‌شده اجرا نکرده‌ام، بنابراین نمی‌توانم بگویم 65 در همه‌جا optimal است. این محدودیت مهم است.

AVIF لزوماً ارزش یک encode دیگر را ندارد

اگر تنها منبع باقی‌مانده WebP با حجم 100 KB باشد و AVIFی که 65/63 را پاس می‌کند 96 KB شود، تبدیل را زیر سؤال می‌برم. صرفه‌جویی 4 KB ممکن است ارزش یک نسل lossy دیگر و پیچیدگی پردازش اضافه را نداشته باشد.

اما اگر همان WebP با حجم 100 KB با حفظ policy کیفیت به AVIF با حجم 65 KB تبدیل شود، trade-off در صفحات پرتصویر بسیار جذاب‌تر است.

تبدیل codec باید به دو سؤال جدا جواب بدهد: آیا اعوجاج اضافه قابل قبول است، و آیا کاهش حجم به‌اندازه کافی مهم است؟ پاس کردن اولی دومی را تضمین نمی‌کند.

قاعده‌ای که اکنون استفاده می‌کنم

اگر بهترین original را داشته باشم، مستقیم از آن encode می‌کنم و برای این workload وب از 60/58 استفاده می‌کنم. اگر WebP lossless باشد نیز 60/58. اگر تنها فایل باقی‌مانده مشتق شناخته‌شده lossy باشد، بودجه سخت‌گیرانه‌تر نسل دوم، یعنی فعلاً 65/63، را به کار می‌برم. اگر AVIF فقط کمی حجم را کم کند، نگه داشتن WebP موجود را بررسی می‌کنم.

درس اصلی این نیست که WebP به عدد مخصوص SSIMULACRA2 نیاز دارد. یک full-reference quality metric فقط به سؤالی پاسخ می‌دهد که تصویر reference آن نمایش می‌دهد.

اگر reference قبلاً اطلاعات از دست داده باشد، score بالا یعنی «نزدیک به همین reference»، نه «نزدیک به تصویری که قبل از آن وجود داشت». وقتی provenance تصویر را بخشی از سیاست فشرده‌سازی کردم، thresholdها دیگر تنظیمات دل‌بخواهی codec به نظر نرسیدند؛ آن‌ها به بودجه‌هایی برای نسل‌های مختلف loss تبدیل شدند.