5.3. פורמטים של פיקסלים¶
אלגוריתם שמזהה קצוות מצפה שכל פיקסל יחזיק ערך בהירות. אלגוריתם שעוקב אחר עצם צבעוני מצפה שכל פיקסל יישא צבע. אלגוריתם שמבצע סגירה מורפולוגית מצפה שכל פיקסל יהיה דלוק או כבוי. פורמט הפיקסלים שאובייקט Image נושא – אחד מתוך הקטלוג שמנו חיישני הראייה (Vision Sensors) – הוא מה שהופך את הציפיות האלה לניתנות לבדיקה מראש: הפורמט קובע, מבעוד מועד, באיזו צורה נמצאים הפיקסלים, ואילו אלגוריתמים יכולים לפיכך לרוץ עליהם ללא שלב המרה.
עמוד זה עוסק באופן שבו האילוץ הזה בא לידי ביטוי בפועל. הבחירה בפורמט הנכון תלויה במה שצינור העיבוד עומד לעשות, ושיטות ההמרה בין פורמטים הן הדרך שבה צינור עיבוד הזקוק ליותר מאחד מהם משחיל יחד את השלבים.
חמשת פורמטי הפיקסלים הלא-דחוסים וכיצד הבתים שלהם נארזים. JPEG ו-PNG אינם מצוירים כאן משום שהם זרמים דחוסים באורך משתנה ולא רשתות פיקסלים בגודל קבוע.¶
5.3.1. סוס העבודה של גווני האפור¶
רוב הראייה הממוחשבת הקלאסית מסתכמת בעבודה עם ערכי בהירות. זיהוי קצוות, התאמת תבניות, פענוח AprilTag, הערכת זרימה אופטית, האופרטורים המורפולוגיים, ניתוח רכיבים (blob) – כולם, ברמה שבה האלגוריתמים פועלים, בוחנים עד כמה כל פיקסל בהיר וכיצד הבהירות משתווה לבהירות של פיקסלים סמוכים. צבע הסצנה לעיתים קרובות שימושי ליישום שקורא להם, אך האלגוריתמים עצמם אינם זקוקים לו.
פורמט גווני האפור מספק לאלגוריתמים בדיוק את זה, ללא תקורה. בית אחד לכל פיקסל מחזיק ערך בהירות מ-0 (שחור) ועד 255 (לבן). הפורמט הוא בחצי מהגודל של RGB565 ו-YUV422 ובשליש מהגודל של RGB888, כך שכל פעולה עוברת על פחות נתונים – גם מהר יותר וגם עם פחות לחץ על החוצץ (buffer). במצלמות הקטנות יותר, שבהן חוצץ הפריימים (frame buffer) מתחרה עם שאר הסקריפט על זיכרון RAM, הבדל זה בנפח עשוי להכריע אם צינור עיבוד מתאים בכלל. אם צבע אינו הרמז שהאלגוריתם זקוק לו, גווני אפור הם התשובה הנכונה.
5.3.2. צבע באמצעות RGB565¶
כאשר צבע הוא כן הרמז – מעקב אחר סמן צבעוני, הבחנה בין תפוחים אדומים לירוקים, איתור רכיב ממשק לפי גוונו – שני בתים לכל פיקסל קונים מספיק צבע לסוגי הסיווג שהאלגוריתמים מבצעים. RGB565 הוא פורמט הצבע ברירת המחדל במצלמה, וזה שהשיטות מודעות-הצבע בממשק מצפות לו.
עיבוד (rendering) של פריים עם הערות – ציור תיבות זיהוי, כתיבת טקסט אבחוני, הצגת הפריים על מסך או שליחתו למציג מרוחק – מצריך אף הוא באופן טבעי RGB565. תצוגה המקדימה של ה-IDE, בקרי התצוגה שעל הלוח, ורוב יעדי הרשת או צורכים את הפורמט ישירות או ממירים ממנו בזול.
5.3.3. Bayer כפורמט אחסון¶
תמונת Bayer היא הפלט הגולמי של החיישן, לפני שה-ISP ביצע עליה דה-בייאר (debayer) לכדי ייצוג צבע מוגמר. כל פיקסל הוא בית אחד המחזיק ערוץ צבע בודד – זה שמסנן הצבע באותו מיקום בפסיפס העביר. הדבר הופך תמונת Bayer לזהה בגודלה לתמונת גווני אפור ולשליש מהגודל של RGB888, מה שמתיישב עם מה ש-Bayer באמת שימושי לו: אחסון פריימים רבים בבת אחת כאשר זיכרון RAM הוא האילוץ המגביל.
המלכוד הוא שהאלגוריתמים במודול image אינם פועלים על תמונות Bayer ישירות. ללא דה-בייאר, אף פיקסל אינו נושא מספיק מידע כדי לקבל החלטה לגבי צבע בכוחות עצמו, והתבניות שהאלגוריתמים מחפשים – קצוות, פינות, רכיבים (blobs) – יעוותו על ידי הפסיפס. הדרכים היחידות לקרוא או לשנות תמונת Bayer הן get_pixel() ו-set_pixel(); כל השאר מצפים לייצוג מוגמר.
התבנית הנובעת מכך היא לאחסן פריימים כ-Bayer לכל זמן שהם צריכים לשבת בתור ולהמיר כל אחד מהם לגווני אפור או ל-RGB565 ברגע שהעיבוד שלו ממש מתחיל. ההמרה עולה מחזורי CPU אך חוסכת את זיכרון ה-RAM שאחרת היה תפוס בהחזקת פריימים מוגמרים למשך כל חיי היישום.
הערה
הפעולות היחידות של מודול image על פיקסלי Bayer ישירות הן get_pixel(), set_pixel(), ונתיב קידוד ה-JPEG שמזין את התצוגה המקדימה של ה-IDE או מציג מרוחק. ציור, ניתוח וסינון כולם מחייבים תחילה המרה לגווני אפור, RGB565 או בינארי.
5.3.4. YUV422 לצינורות עיבוד שרוצים את שניהם¶
YUV422 מפריד את המידע של כל פיקסל לערוץ בהירות (luminance, Y) ולשני ערוצי כרומיננס (U ו-V), ומבצע תת-דגימה של הכרומיננס כך שזוגות פיקסלים סמוכים חולקים U יחיד ו-V יחיד. הבתים לכל פיקסל מסתכמים בממוצע בשניים – כמו ב-RGB565 – אך הם מסודרים כך שערוץ ה-Y הוא כבר תמונת גווני אפור רציפה של 8 סיביות היושבת בהיסטים ידועים בחוצץ (buffer).
פריסה זו היא בדיוק מה שצינור עיבוד רוצה כאשר חלק משלביו הם עבודת גווני אפור וחלקם זקוקים לצבע. קריאת ערכי ה-Y ישירות עבור שלבי גווני האפור חוסכת את עלות ההמרה המפורשת; ערוצי ה-U וה-V נמצאים שם כאשר שלב מאוחר יותר באמת זקוק לצבע. מחוץ לתבנית הספציפית הזו, RGB565 הוא בדרך כלל הבחירה הפשוטה יותר לצבע וגווני אפור הם הבחירה הפשוטה יותר לעבודה מבוססת בהירות בלבד – הערך של YUV422 נובע מהיותו טוב בשניהם בו-זמנית.
הערה
מודול image פועל על YUV422 באופן מוגבל יותר מאשר על גווני אפור, RGB565 או בינארי – קריאות ישירות של ערוץ ה-Y לעבודת גווני אפור ונתיב קידוד ה-JPEG שמזין את התצוגה המקדימה של ה-IDE או מציג מרוחק. שיטות מודעות-צבע מצפות ל-RGB565; פריימים של YUV422 זקוקים להמרה מפורשת לפני ניתוח צבע או ציור.
5.3.5. בינארי, מסכות ופלט עם סף¶
תמונה בינארית היא סיבית אחת לכל פיקסל: כל פיקסל הוא או 0 או 1. הפורמט מופיע לעיתים נדירות כקליטה מחיישן; במקום זאת הוא מופיע כפלט הטבעי של הפעלת סף (thresholding) (שבה בדיקת צבע או בהירות מסווגת כל פיקסל ל“כן, תואם“ או ”לא, לא תואם“) וכקלט הטבעי לפעולות מורפולוגיות ולארגומנט mask שרבות מהשיטות מקבלות.
היתרון המעשי של הפורמט הוא גודלו. תמונה בינארית היא שמינית מנפח תמונת גווני אפור, כך שנשיאת מסכה גדולה – בחירה לכל פיקסל אילו מיקומים פעולה כלשהי במורד הזרם צריכה לגעת בהם – היא זולה. העובדה שפעולות רבות מקבלות תמונה בינארית כארגומנט מילת-מפתח mask= היא הצד השני של אותו עניין: הפורמט קטן, ושרשור הפלט הבינארי של שלב אחד אל קלט המסכה של שלב אחר הוא תבנית צינור עיבוד נפוצה.
5.3.6. JPEG ו-PNG בגבול¶
אובייקטי Image מסוג JPEG ו-PNG שונים מהאחרים שבקטלוג. הם אינם רשתות פיקסלים; הם זרמי בתים דחוסים שמקודדים נתוני פיקסלים בצורה שפעולות ברמת הפיקסל אינן יכולות לקרוא. קריאה ל-get_pixel() על JPEG אינה מחזירה את הפיקסל במיקום נתון; הפיקסל אינו יושב פרוס בשום מקום בחוצץ כדי שהשיטה תשלוף אותו.
JPEG ו-PNG מופיעים בגבול של עיבוד התמונה, היכן שנתוני הפיקסלים עוזבים את המצלמה או נכנסים אליה בצורה דחוסה. שמירת פריים לדיסק כ-JPEG שומרת על קובץ קטן; שליחת פריים ברשת כ-JPEG שומרת על תשדורת זולה; טעינת פריים ייחוס מקובץ JPEG מאפשרת לו לשבת על הדיסק בצורה קטנה בהרבה מכפי שהפיקסלים הגולמיים היו תופסים. עבור כל אחד ממקרי השימוש האלה הייצוג הדחוס הוא התשובה הנכונה. אולם כדי לבצע עיבוד ממשי כלשהו על JPEG, היישום ממיר אותו תחילה לפורמט בר-עבודה – והמרה זו היא היכן שהבתים הדחוסים מורחבים לפיקסלים והיכן שניפוח החוצץ (JPEG של 30 KB יכול להפוך ל-600 KB של RGB565) באמת מתרחש.
5.3.7. המרה בין פורמטים¶
נתיב ההמרה הוא מה שתופר פורמטים שונים לצינור עיבוד יחיד. חמש שיטות במחלקת Image לוקחות תמונה קיימת ומחזירות תמונה חדשה בפורמט אחר:
to_grayscale()מפיקה תמונה של בית בודד לכל פיקסל, הפורמט שהאלגוריתמים הקלאסיים רוצים.to_rgb565()מפיקה את פורמט הצבע של שני בתים לכל פיקסל שהן השיטות מודעות-הצבע והן התצוגה המקדימה של ה-IDE דוברות.to_bitmap()מפיקה תמונה בינארית של סיבית אחת, הפורמט שהמורפולוגיה וארגומנטיmaskמקבלים.to_jpeg()מפיקה תמונה דחוסת-JPEG המתאימה לשמירה או לשידור.to_png()מפיקה תמונה דחוסת-PNG כאשר מעדיפים קידוד ללא אובדן על פני הקבצים הקטנים יותר של JPEG.
כל המרה רצה במקום (in place) כברירת מחדל: החוצץ של תמונת המקור נדרס בתוצאה המומרת, והפיקסלים המקוריים של המקור נעלמים לאחר שהקריאה מסתיימת. זוהי האפשרות הזולה ביותר הן עבור CPU והן עבור זיכרון, והיא התשובה הנכונה כאשר פריים המקור לא יידרש לשום דבר אחר.
כאשר המקור עדיין כן נדרש – כאשר שלב מאוחר יותר בצינור העיבוד חייב לראות את הפריים המקורי – שני ארגומנטי מילת-מפתח עוקפים את ברירת המחדל של המרה במקום. copy=True מקצה חוצץ נפרד עבור התמונה המומרת בערימת (heap) של Python ומשאיר את המקור שלם. copy_to_fb=True מבצע את אותה הקצאה אך מציב אותה בחוצץ הפריימים (frame buffer) במקום בערימה – וזה מה שיישום נשען עליו כאשר התמונה המומרת צריכה לנחות בתצוגה המקדימה של ה-IDE, מאחר שה-IDE קורא מחוצץ הפריימים.
שתי שיטות נוספות מפיקות תמונות RGB565 הצבועות באמצעות פלטה במקום בהמרה ישירה. to_rainbow() ממפה כל ערך קלט חד-ערוצי לצבע לאורך מפל חלק שעובר דרך הספקטרום הנראה. to_ironbow() ממפה כל ערך קלט לפלטת מצלמת החום הלא-ליניארית שעוברת משחור דרך אדומים כהים וכתומים ועד לבן. שתיהן הן כלי הדמיה (visualisation) ולא כלי מדידה; המטרה היא להפוך תמונה חד-ערוצית, שערכיה הגולמיים אחרת היו בלתי נראים לעין, לקריאה במבט חטוף.
5.3.8. גודל חוצץ¶
פרט אחרון אחד לגבי פורמטים שכדאי להבהיר במפורש. size() תמיד מדווחת על גודל חוצץ הבתים, לא על מספר הפיקסלים. עבור פורמטים לא-דחוסים זה נובע ישירות מהמידות ומהבתים-לכל-פיקסל: width * height * bytes_per_pixel. עבור JPEG ו-PNG זהו גודל הזרם הדחוס, המשתנה מפריים לפריים בהתאם למה שהסצנה מכילה. קוד שמקצה חוצצים מתקציבי בתים משתמש ב-size() עבור המקרה הראשון; קוד שמזרים פריימים דחוסים החוצה מהמצלמה קורא אותה לאחר כל דחיסה כדי לדעת כמה בתים הזרם באמת מכיל.