5.5. אזורים ומסכות¶
כל פעולה במודול image נוגעת כברירת מחדל בכל פיקסל של תמונת המקור שלה. זוהי ההתנהגות הפשוטה ביותר לתיאור, והנכונה כאשר תפקיד האלגוריתם משתרע באמת על פני הפריים כולו – תיקון צבע אחיד, היסטוגרמה גלובלית, מעבר קידוד לשם שידור. אך רוב האלגוריתמים בפועל רוצים להסתכל על פחות מכך. עוקב רכיבים (blob) שצופה בסמן צבעוני אכפת לו מהחלק בסצנה שבו הסמן יכול להופיע, ולא מהקיר שמאחוריו. מעבר ניקוי מורפולוגי בטוח רק על פני הפיקסלים ששלב מוקדם יותר סימן כמועמדים. גלאי פנים עשוי לרוץ רק בתוך התיבה התוחמת שגלאי גס יותר כבר צמצם אליה. מודול image תומך בעבודה זו באמצעות שני מנגנונים שמתחמים פעולה לתת-קבוצה של פיקסלים: אזורי עניין מלבניים, ומסכות בינאריות. הם מתלכדים בחופשיות, וכמעט כל מתודה שנוגעת בפיקסלים מקבלת אחד מהם – או את שניהם – כארגומנט מילת מפתח.
5.5.1. אזורי עניין¶
אזור עניין הוא מלבן של פיקסלים המכונה על ידי הרביעייה (x, y, w, h) שהוצגה בעמוד הקואורדינטות. כשלושים מתודות בממשק מקבלות ארגומנט מילת מפתח roi; כשהוא נוכח, הפעולה רצה רק על הפיקסלים שבתוך אותו מלבן ומשאירה את שאר התמונה ללא נגיעה. כש-roi הוא None או מושמט, הפעולה רצה על פני התמונה כולה – בדיוק כאילו הועבר roi=(0, 0, width, height).
בקוד מילת המפתח יושבת לצד כל ארגומנט אחר שהפעולה מקבלת:
# Compute a histogram over a centred crop of the image.
h = img.get_histogram(roi=(64, 64, 128, 128))
הדבר הראשון שאזורי העניין מקנים הוא בקרת תוצאות חיוביות שגויות. עוקב צבע שמסתכל רק על השולחן לעולם לא יופעל על ידי החולצה שחולפת על פניו; גלאי קצוות שרץ רק בתוך אזור העבודה המוגדר לעולם לא ידווח על קצוות מתקן ההרכבה של המצלמה עצמו. צמצום אזור החיפוש לחלק בסצנה שאכפת לאלגוריתם ממנו בפועל הוא השיפור הזול ביותר שצינור עיבוד יכול לעשות לאמינות שלו.
הדבר השני שהם מקנים הוא צינור עיבוד מגס לעדין. אובייקטי תוצאת זיהוי – blob, rect, apriltag, וכן הלאה – חושפים את התיבות התוחמות שלהם כאותה רביעייה (x, y, w, h) ש-roi מקבל. כך ששלב ראשון גס יכול להחזיר תיבה תוחמת, התיבה נופלת ישירות אל ה-roi של השלב הבא, והשלב השני רץ על פני האזור הצר יותר. כל צמצום מתקדם גם מאיץ את השלב הבא וגם הופך את תוצאותיו לאמינות יותר, מכיוון שמרחב החיפוש כבר סונן.
5.5.2. מסכות בינאריות¶
מלבן הוא הצורה הנכונה כאשר אזור העניין מיושר לצירים. כשהוא לא – אזור מעוקל, אזור לא קמור, הפיקסלים ששלב מוקדם יותר סיווג כ“התאמות“ – יש לומר לפעולה לתחם את עצמה לדפוס שרירותי של פיקסלים במקום. המנגנון לכך הוא מסכה בינארית: Image נפרדת, באותם ממדים כמו המקור, המשמשת כמתג הפעלה/כיבוי לכל פיקסל. פיקסל שאינו אפס במסכה אומר ”כלול את פיקסל המקור התואם“; פיקסל אפס אומר ”השאר את פיקסל המקור ללא נגיעה.“
מסכה היא בדרך כלל תמונת BINARY – הפורמט של ביט אחד לפיקסל שקיים בדיוק למטרה זו – אך כל תמונת ערוץ יחיד תעבוד, מכיוון שהצרכן מתייחס לכל ערך שאינו אפס כמופעל.
מתודות סינון, ספים וחשבון מקבלות ארגומנט מילת מפתח mask. הצורה זהה בכל אחת מהן: תמונה בינארית שהוקצתה בנפרד, באותם ממדים כמו המקור, המועברת אליה.
אזורי עניין ומסכות מתלכדים. העבר את שניהם, והפעולה רצה רק על פיקסלים שנמצאים בתוך אזור העניין וגם מופעלים במסכה. שני המנגנונים נותנים לקוד היישום ידיות עצמאיות – אחת לאזור העניין המלבני, אחת לדפוס השרירותי בתוכו – מבלי לגרום לאף אחת מהצורות לרשת אילוצים מהשנייה.
אזור עניין מגביל פעולה למלבן מיושר לצירים. מסכה מצמצמת אותו עוד יותר לדפוס שרירותי של פיקסלים. השניים מתלכדים: רק פיקסלים שבתוך אזור העניין וגם מופעלים במסכה משתנים.¶
5.5.3. בניית מסכות¶
שלוש מתודות של Image בונות גאומטריות מסכה נפוצות במקום על ידי איפוס הפיקסלים מחוץ לאזור הנבחר:
mask_rectangle()שומרת על מלבן.mask_circle()שומרת על מעגל.mask_ellipse()שומרת על אליפסה.
כל אחת מקבלת (x, y, w, h) (עבור המלבן והאליפסה) או (x, y, radius) (עבור המעגל). קריאה לכל אחת מהן ללא ארגומנטים ממרכזת את הגאומטריה ומתאימה את גודלה למילוי התמונה, וזו הצורה שיישום פונה אליה כאשר המטרה היא אובלי או מעגל פשוט במלוא התמונה שאינו מסתיר דבר מלבד הפינות.
mask = image.Image(img.width(), img.height(), image.BINARY)
mask.clear() # start from all zeros
mask.mask_ellipse() # centred, full-size oval
המסכות המעניינות לעיתים נדירות מגיעות ממתודות ה-mask_* לבדן. הן מגיעות משלבים מוקדמים יותר של צינור העיבוד: מעבר ספים מייצר תמונה בינארית שהפיקסלים שאינם אפס בה מסמנים את ההתאמות, בדיוק הצורה הנכונה להזנה אל ארגומנט ה-mask= של השלב הבא. מעבר ניקוי מורפולוגי מעדן את המסכה הזו מבלי לשנות את צורתה. כל דבר שמסתיים כתמונת ערוץ יחיד הוא בעצמו מסכה תקפה.
5.5.4. כיצד פעולות משנות את התמונה¶
דפוס הנראה בכל קטע קוד בעמודים האחרונים – הפעולה מחזירה את אותה img לשרשור – ראוי להוצאה מפורשת כדי שלא יהיה צורך לחזור עליו בכל פעם שמוצגת מתודה חדשה. שלוש משפחות של מתודות מופיעות בממשק Image, וכל אחת מטפלת בתמונת המקור באופן שונה:
מתודות מפעילות משנות את פיקסלי המקור במקום ומחזירות את אותה תמונה לשרשור. משפחות הציור, החשבון, הספים והסינון כולן מתנהגות כך.
img.gaussian(1)מטשטשת אתimgומחזירה את אותהimg; הקצאה מחדש –img = img.gaussian(1)– אינה מזיקה אך מיותרת.מתודות המרה פועלות במקום כברירת מחדל באותו אופן שבו פועלות מתודות מפעילות, אך הן מקבלות
copy=Trueו-copy_to_fb=Trueכדי להקצות תמונת תוצאה נפרדת כאשר יש לשמר את המקור. המרות הפורמט וההעתקות הגאומטריות הן החברות העיקריות במשפחה זו.מתודות בדיקה קוראות את הפיקסלים ומחזירות אובייקט תוצאה – רשימת מאפיינים שזוהו, היסטוגרמה, קבוצת סטטיסטיקות – מבלי לשנות כלל את תמונת המקור.
החלוקה לשלושה עקבית על פני כל הממשק. ידיעה לאיזו משפחה שייכת מתודה אומרת ליישום למה לצפות מקריאה: האם פיקסלי המקור ישרדו ללא פגע, האם תוקצה תמונת תוצאה נפרדת, והאם ערך ההחזרה הוא המקור עצמו או משהו אחר.