
לנרמל ערכי RGB ב-255 או ב-256?
המתכנת הגרפי פקה ואננן מנתח בשני דרכים נפוצות להמרת ערכי RGB של 8 סיביות למספרי נקודה צפה ובחזרה, ומסביר מתי החלוקה ב-255 היא הבחירה הנכונה ומתי אפשר להצדיק חלוקה ב-256.
המרת ערכי צבע של 8 סיביות למספרי נקודה צפה ובחזרה היא אחת ההחלטות הראשונות בכל תוכנת עיבוד תמונה, ובנושא הזה יש שתי אסכולות. המתכנת הגרפי פקה ואננן פרסם בבלוג שלו 30fps.net ניתוח של השאלה, שבו הוא משווה את החלוקה הסטנדרטית ב-255 לחלופה שמוסיפה הטיה של 0.5 ומחלקת ב-256.
שתי הנוסחאות
הגישה הסטנדרטית ממירה פיקסלים בעזרת img / 255.0 וכותבת אותם בחזרה בעזרת trunc(result * 255 + 0.5): המספר 0 ממופה ל-0.0 ו-255 ל-1.0 — בדיוק כמו שהמעבדים הגרפיים נוהגים בפורמטים מנורמלים ללא סימן. החלופה משתמשת ב-(img + 0.5) / 256.0 להמרה וב-trunc(result * 256) לכתיבה, וכך כל ערך נקודה צפה ממוקם בדיוק באמצע בין שני מספרים שלמים.
המחיר המעשי של החלופה הוא ששחור אינו 0.0 עוד: המספר 0 מפוענח ל-0.001953125, כך שקוד שמזהה פיקסלים שחורים חייב להכיר את הקבוע הזה ונהיה כבול לטווח של 8 סיביות. המחבר מציין שבנוסחה הסטנדרטית התמונה חוזרת ללא אובדן, ושחוסר הדיוק שלה — 128/255 שווה בערך ל-0.501961 — נשאר בסיבית הפחות משמעותית, בשגיאה יחסית של כ-0.00001 אחוז.
תאים בחצי רוחב בקצוות
החולשה של הנוסחה הסטנדרטית נמצאת בקצוות. מכיוון שטווח הנקודה הצפה שלה רחב מעט מ-[0, 1], התאים שמתעגלים ל-0 ול-255 הם ברוחב מחצית מהאחרים, ולכן רעש בהתפלגות אחידה מייצר את ערכי הקצה בתדירות כפולה פחות. הפוסט מדגים זאת בהיסטוגרמה של מיליון ערכים אקראיים, ואז טוען שלעתים רחוקות יש לכך משמעות: ערכים מעט מחוץ לטווח עדיין מתעגלים למספר השלם הנכון, והפחתת 0.005 משחור מסתיימת ב-0 בשתי השיטות.
בשפת תורת הקוונטיזציה מדובר בשני מקודדים: mid-riser עם 255 רמות ו-mid-tread עם 256. שגיאת השחזור קטנה במעט בחלוקה ב-256 — שגיאה מוחלטת ממוצעת של 1/1024 מול 1/1020 — אך רק כשקוד אחד שולט גם בקידוד וגם בפענוח. טעינת תמונות של אחרים עם מקדם הסקלה החלופי היא, בתיאוריה, פענוח שגוי, והיא מערבבת בין שני המקודדים.
מה כדאי לעשות
המסקנה מעשית בכוונה: אם אתם מעבדים תמונות שלא יצרתם, נרמלו ב-255. חלוקה ב-256 מוצדקת כשאתם שולטים גם בשמירה וגם בטעינה, אינכם זקוקים לכך שאפס ימופה לאפס, ומסכימים לכבול את קוד העיבוד לטווח של 8 סיביות. הפוסט דן גם ב'שיטה הטובה ביותר' המאוחרת של כריס לומונט, שמפענחת עם k/255.0 ומקודדת עם clamp(floor(x * 256), 0, 255), ושומרת על טווח מלא ועל תאים שווים במחיר של בערך פי שניים שגיאת קוונטיזציה, בשיא בקצוות.
SiTech — פיתוח אתרים בכוח ה-AI
אנחנו בונים אתרים מהירים ומודרניים ומשלבים AI בתהליכי עבודה אמיתיים. יש לכם פרויקט או שאלה? נשמח לעזור.