5.9. פעולות אריתמטיות

משפחת הציור בחלק הקודם מציירת לתוך תמונה. המשפחה האריתמטית משלבת שתי תמונות לתוך תמונה שלישית – מחברת את ערכי הפיקסלים שלהן יחד, מחסירה אחת מהשנייה, לוקחת את המינימום או המקסימום בכל מיקום. הקבוצה הקטנה הזו של פעולות אריתמטיות פיקסל-אחר-פיקסל היא הבסיס שעליו בנויים הפרשי פריימים, חיסור רקע, ערימת חשיפות, וחופן דפוסים קלאסיים נוספים.

המשפחה האריתמטית במחלקת Image קטנה מספיק כדי למנותה בבת אחת:

  • add()self + other לכל פיקסל, חתוך למקסימום של הפורמט.

  • sub()self - other לכל פיקסל, חתוך ל-0 בתחתית.

  • rsub()other - self לכל פיקסל, חתוך ל-0 (אותה אריתמטיקה כמו sub עם האופרנדים הפוכים).

  • min() – מינימום של שני הערכים לכל פיקסל.

  • max() – מקסימום לכל פיקסל.

  • difference()|self - other| לכל פיקסל, ההפרש המוחלט.

בנוסף לשתי פעולות קשורות על תמונה בודדת:

  • invert() – מחליפה כל פיקסל ב-255 - pixel (או המקסימום המקביל לפורמט).

  • negate() – כינוי ל-invert().

Two horizontal gradient bars at the top representing source images A and B -- A going dark to bright left-to-right, B going bright to dark left-to-right. Below them, five gradient bars representing the result of each pairwise operation applied to A and B: A.add(B) appears uniform white because every position sums past 255 and clips; A.sub(B) is zero on the left half and brightens toward the right; A.difference(B) shows a V shape, bright on each end and dark in the middle; A.min(B) is dark on the ends and brighter in the middle; A.max(B) is bright on the ends and grey in the middle.

שני מדרגי מקור A ו-B, והתוצאה של כל פעולה זוגית שהוחלה עליהם. כל פעולה רצה מיקום אחר מיקום – מה שמופיע בתוצאה בכל מיקום בודד תלוי רק בשני פיקסלי המקור באותו מיקום.

5.9.1. שתי צורות אופרנד

כל אחת משיטות שתי-התמונות מקבלת אחת משתי הצורות עבור האופרנד השני שלה:

  • Image נוספת באותם ממדים. האריתמטיקה רצה מיקום אחר מיקום – התוצאה ב-(x, y) היא הפעולה שהוחלה על פיקסלי המקור ב-(x, y) של שתי התמונות.

  • ערך סקלרי – מספר שלם עבור גווני אפור, טאפל (r, g, b) עבור RGB565. אותו סקלר חל בכל מיקום.

הצורה הסקלרית שימושית כאשר היישום רוצה להזיז כל פיקסל בכמות קבועה. img.add(40) מבהירה את כל התמונה ב-40; img.sub((20, 20, 20)) מכהה כל פיקסל ב-20 לכל ערוץ; img.max(50) מרימה כל פיקסל מתחת ל-50 עד ל-50 ומשאירה את השאר ללא שינוי – סוג הפעולה שהופך רצפת חיישן כמעט שחורה לאפור כהה אחיד שעבורו השלבים הבאים יכולים לעבוד.

5.9.2. חיתוך

ערכי הפיקסלים נשארים בתוך טווח הפורמט לאורך כל פעולה. עבור ערוץ של 8 סיביות פירוש הדבר 0255: כל דבר שהיה גולש מעבר ל-255 נחתך בחזרה ל-255, וכל דבר שהיה יורד מתחת ל-0 נחתך כלפי מעלה ל-0. אין גלישה מעגלית.

הבחירה הזו חשובה בפועל. add שמבהירה פיקסלים לעולם לא מייצרת ארטיפקט החשכה פתאומי בקצה הבהיר שבו האריתמטיקה הייתה אחרת גולשת; sub שמכהה פיקסלים לעולם לא מייצרת ארטיפקט הבהרה פתאומי בקצה הכהה שבו היא הייתה אחרת זולגת מתחת. התוצאות נשארות משמעותיות חזותית במחיר של אובדן מידע מסוים בקצוות הרוויים.

החיתוך הוא גם הסיבה ש-sub ו-rsub מחזירות תוצאות שונות זו מזו. img_a.sub(img_b) נותנת את החלק של a שבהיר יותר מ-b ואפס בכל מקום אחר; img_a.rsub(img_b) נותנת את החלק של b שבהיר יותר מ-a. כל אחת שימושית לזיהוי שינוי חד-צדדי – אם ליישום אכפת רק מפיקסלים שהתבהרו, או רק מפיקסלים שהוכהו – אך אף אחת לא לוכדת את כל השינוי בין שני פריימים.

5.9.3. פעולת ההפרש

עבור זיהוי שינוי דו-צדדי, הפעולה שיש לפנות אליה היא difference(), שמחשבת |self - other| בכל מיקום – ההפרש המוחלט, נטול סימן. כל פיקסל שהשתנה בכיוון כלשהו מופיע כערך שאינו אפס בתוצאה, כאשר הגודל פרופורציונלי לכמה שהשתנה באותו מיקום.

התכונה הזו – שאינה אפס בדיוק היכן ששתי התמונות אינן תואמות – היא מה שהופך את difference לסוס העבודה של זיהוי שינוי פריים-אחר-פריים. פריים ייחוס שנשמר באתחול ולכידה טרייה, מורצים דרך difference, מייצרים תמונה שהפיקסלים שאינם אפס בה מסמנים כל מיקום שבו משהו בסצנה זז או שינה את בהירותו.

5.9.4. תיחום באמצעות מסכה

כל השיטות האריתמטיות מקבלות את ארגומנט מילת המפתח mask שהוצג בעמוד אזורים-ומסכות. כאשר מועברת מסכה, הפעולה רצה רק במיקומים שבהם המסכה אינה אפס; בכל מקום אחר תמונת היעד נשארת ללא שינוי.

הרכבה זו מופיעה בשני דפוסים. הראשון הוא הגבלת פעולה לאזור ידוע: חיבור שני פריימים יחד רק בתוך התיבה התוחמת של סמן שזוהה, למשל. השני הוא בנייה של פריים מורכב חלק אחר חלק – min על רצף של פריימים בתוך מסכת קדמת תמונה, max על אותו רצף בתוך המסכה המשלימה – דפוס מסוג זה.

5.9.5. במקום, ושימור הקלטים

כל השיטות האריתמטיות עוקבות אחר מוסכמת הפעולה שנקבעה קודם לכן: כל אחת משנה את תמונת המקור במקום ומחזירה את אותה תמונה לצורך שרשור. פיקסלי המקור נעלמים לאחר הקריאה – מוחלפים בתוצאת הפעולה מול מה שהועבר כאופרנד השני.

כאשר היישום צריך לשמר את שני הקלטים, הדפוס הבטוח הוא להעתיק אחד מהם תחילה:

diff = current.copy()       # leaves current intact
diff.difference(reference)  # diff now holds the absolute difference

הדפוס הזה – העתק, ואז פעל – הוא עמוד השדרה של כל צינור הפרשי פריימים, שבו פריים הייחוס חייב לשרוד את ההשוואה כדי שניתן יהיה להשתמש בו שוב בפריים הבא שנלכד.

עם שש פעולות שילוב, שתי פעולות על תמונה בודדת, סוס עבודה של הפרש מוחלט, ומילת המפתח mask לתיחום, ערכת הכלים של אריתמטיקת הפיקסלים מכסה את שילובי הבהירות-והערוצים שראייה ממוחשבת קלאסית צריכה. הכלים הנותרים הדומים-לאריתמטיקה על פני השטח עובדים סיבית אחר סיבית במקום ערך אחר ערך.