5.21. שינוי קנה מידה, היפוך וחיתוך¶
תת-הסעיפים הקודמים פעלו כולם על פיקסלים במיקומים שבהם הם התחילו. משפחת הטרנספורמציה משנה זאת. שינוי קנה מידה שולח כל פיקסל קלט למיקום פלט שונה, אולי לכמה מיקומי פלט בבת אחת (בעת הגדלה) או למיקום משותף עם כמה פיקסלי קלט אחרים (בעת הקטנה). היפוך וסיבוב עושים את אותו הדבר באמצעות מיפוי שונה. חיתוך שומר על תת-קבוצה מלבנית של פיקסלי קלט ומשליך את השאר.
מודול ה-image חושף את אותה משפחה דרך שלוש מתודות שחולקות את רוב הארגומנטים ואת רוב ההתנהגות שלהן:
copy()– מפיק עותק של התמונה, אולי משונה קנה מידה, חתוך או מכוון מחדש.crop()– אותה פעולה כמוcopy, אך עם הציפייה שהיישום עומד לבחור תת-מלבן מתוך המקור.scale()– שוב אותו דבר, עם הציפייה שהיישום עומד לשנות את גודל התוצאה.
השלוש חולקות את אותם ארגומנטים ואת אותו מנגנון טרנספורמציה; ההבדל הוא היכן נוחתת התוצאה כברירת מחדל. copy() מפיקה תמונה חדשה, בעוד crop() ו-scale() משנות את המקור במקומו.
5.21.2. אינטרפולציה: AREA, BILINEAR, BICUBIC¶
כאשר שינוי קנה המידה שולח כל פיקסל פלט למיקום שאינו מתיישר עם אף פיקסל קלט בודד, על המתודה לבחור איזה ערך לכתוב. שלושה דגלים שולטים כיצד:
image.BILINEAR מבצע אינטרפולציה בין ארבעת פיקסלי הקלט הקרובים ביותר משוקללים לפי מרחקם ממיקום הפלט. התוצאה חלקה יותר מ-nearest-neighbour, ללא שיניות נראות לעין בקווים אלכסוניים, אך החשבון הנוסף עולה פי ארבעה בקירוב ממעבר nearest-neighbour. הבחירה הנכונה עבור רוב עבודות ההגדלה ועבור כל גורם קנה מידה לא שלם.
image.BICUBIC מבצע אינטרפולציה בין ששה-עשר פיקסלי הקלט הקרובים ביותר באמצעות עקומה קובית, מה שמפיק תוצאות חלקות עוד יותר במחיר עוד חשבון. האיכות הטובה ביותר עבור היישומים רגישי-העלות הזקוקים לכך; לעיתים רחוקות שווה את החישוב הנוסף עבור פריימים חיים שה-IDE רק יציג.
image.AREA ממצע כל פיקסל קלט הנופל בתוך טביעת הרגל של פיקסל הפלט – האלגוריתם הנכון להקטנה. Bilinear ו-bicubic הם אינטרפולטורים: הם מעריכים ערך בין פיקסלי המקור, שזה מה שהגדלה צריכה, אך בעת הקטנה כל פיקסל פלט מכסה פיקסלי מקור רבים ואינטרפולטור קורא רק את המעטים הקרובים ביותר – הפרטים שהוא מדלג עליהם חוזרים כשיבושי תיאום (aliasing). image.AREA מקפל כל פיקסל מכוסה לתוך הממוצע במקום זאת.
אלגוריתם שינוי קנה המידה כברירת מחדל ללא שום hint הוא nearest-neighbour, שהוא הזול ביותר והתשובה הנכונה כאשר המקור כבר ברזולוציית הפיקסלים של היעד.
5.21.3. כיוון: היפוכים וסיבובים¶
דגלי הכיוון הם קבוצה קטנה של טרנספורמציות בוליאניות המתחברות בחופשיות זו עם זו ועם דגלי האינטרפולציה:
image.VFLIPהופך את התמונה אנכית (העליון הופך לתחתון).image.HMIRRORמשקף אותה אופקית (השמאל הופך לימין).image.TRANSPOSEמחליף את צירי ה-x וה-y (שורות הופכות לעמודות).
רוב הסיבובים נובעים מהרכבת שלושת אלה. המודול חושף גם קיצורים בעלי שם:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
בקוד:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. טיפול ביחס גובה-רוחב¶
כאשר יחס הגובה-רוחב של המקור אינו תואם למלבן שאליו הוא מצויר, שלושה דגלים מחליטים מה לעשות עם אי-ההתאמה:
image.SCALE_ASPECT_KEEP משמר את יחס הגובה-רוחב של המקור ומבצע letterbox לתוצאה – המקור משונה קנה מידה עד שהוא נכנס בתוך היעד, כשפיקסלים ריקים (אפס) ממלאים את שאר היעד. הבחירה הנכונה כאשר שמירה על המקור ללא עיוות חשובה יותר ממילוי כל הפלט.
image.SCALE_ASPECT_EXPAND משמר את יחס הגובה-רוחב של המקור וחותך אותו – המקור משונה קנה מידה עד שהוא ממלא את היעד, כשהחלקים החורגים מעבר ליעד נחתכים. הבחירה הנכונה כאשר מילוי כל הפלט חשוב יותר מראיית כל חלק מהמקור.
image.SCALE_ASPECT_IGNORE מתעלם מיחס הגובה-רוחב ומותח את המקור כדי למלא את היעד, ומקבל כל עיוות שזה מכניס. הבחירה הנכונה כאשר היישום כבר התחשב בעיוות – כאשר מידות היעד אינן למעשה מלבן של אותה סצנה, לדוגמה.
ברירת המחדל (ללא דגל יחס מוגדר) זהה ל-SCALE_ASPECT_IGNORE: מתיחה למילוי. יישומים שאכפת להם מיחס הגובה-רוחב מציינים אחד מהשלושה במפורש.
5.21.5. מתי לבחור באיזה¶
רוב שינויי הגודל משתמשים ב-scale() עם זוג x_scale / y_scale ו-hint של אינטרפולציה:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
רוב הסיבובים משתמשים באותה קריאה עם hint=image.ROTATE_90 או דומה.
חיתוך משתמש ב-crop() עם roi שאינו ברירת מחדל:
img.crop(roi=(40, 30, 200, 150))
כאשר המקור חייב לשרוד את הפעולה – לכידת פריים ייחוס, לקיחת תמונה ממוזערת של פריים שעומד לעבור עיבוד הרסני – copy() מפיקה את התוצאה כתמונה חדשה ומשאירה את המקור ללא נגיעה:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
ברירת מחדל זו היא ההבדל האמיתי שמאחורי שלושת השמות: scale ו-crop עוברים טרנספורמציה במקומם, copy מקצה. מילות המפתח למיקום התוצאה מגשרות על הפער: copy=True ב-scale או crop מקצה את התוצאה כחוצץ (buffer) נפרד בערימה במקום לדרוס את המקור, ו-copy_to_fb=True בכל אחת מהשלוש נוחת אותה בחוצץ הפריימים (frame buffer) עבור התצוגה המקדימה ב-IDE.