5.30. התאמת תבנית¶
המזהים שכוסו עד כה עונים על שאלות בנוגע לתוכן של פריים בודד: היכן נמצאים הרכיבים (blobs), לאן הולכים הקווים, מה אומר קוד מודפס. סוג שונה של שאלה משווה תמונה אחת מול תמונה אחרת. האם אזור זה בפריים שנלכד נראה כמו התבנית הייחוס ששמרתי בזמן הכיול? שיטות ההתאמה עונות על השאלה הזו.
ניתוח טונאלי וסטטיסטי הציג את get_similarity() עבור השאלה הקשורה – עד כמה דומות שתי תמונות בגודל זהה באופן כללי? – כאשר SSIM היא המדד הבסיסי. שאלת ההתאמה הנותרת היא שאלת המיקום: לא ”עד כמה דומות שתי התמונות האלו“ אלא ”היכן בתוך התמונה הגדולה הזו מופיעה התבנית הקטנה?“ הכלי הנכון לשאלת המיקום הוא התאמת תבנית.
5.30.1. הקריאה הבסיסית¶
find_template() מחפשת את המקום הראשון שבו תמונת תבנית קטנה מופיעה בתוך הפריים שנלכד. המימוש משתמש בקורלציה צולבת מנורמלת (NCC): התבנית מחליקה לאורך הפריים, ניקוד ההתאמה לכל מיקום מחושב מהקורלציה בין פיקסלי התבנית לבין פיקסלי הפריים שמתחתם (מנורמלים מול הממוצעים והשונויות המקומיים כך ששינויי הגבר (gain) לא יטעו את ההתאמה), והמיקום הראשון שניקודו עובר את threshold מוחזר כתיבה תוחמת:
template = image.Image("/sdcard/template.bmp", copy_to_fb=False)
template.to_grayscale()
match = img.find_template(template, threshold=0.7,
search=image.SEARCH_DS)
if match is not None:
img.draw_rectangle(match, color=(255, 0, 0))
השיטה עובדת רק על תמונות בגווני אפור. צלם בגווני אפור (הבחירה הטבעית לכל מצלמה ללא חיישן צבע), או המר במקום באמצעות to_grayscale() לפני הקריאה. אותו דבר חל על התבנית הנטענת מהדיסק: תבנית צבעונית מומרת באמצעות אותה שיטה, והתוצאה היא מה שמנגנון ההתאמה מצפה לו.
threshold הוא מספר עשרוני מ-0.0 עד 1.0. ערך של 1.0 דורש התאמה מושלמת פיקסל-לפיקסל (שלעולם לא קורית עם תמונות אמיתיות שנלכדו), 0.0 מקבל כל דבר, וערכים שבין 0.6 ל-0.8 מכסים את המקרה השכיח שבו התבנית נלכדה בתאורה דומה והסצנה לא השתנתה באופן דרמטי. העלה את הסף כדי לדכא זיהויים שגויים; הורד אותו כדי לקבל התאמות רועשות יותר במחיר של יותר תוצאות מזויפות.
5.30.2. אסטרטגיית חיפוש¶
search בוחר בין שתי אסטרטגיות. image.SEARCH_EX הוא החיפוש הממצה: התבנית מחליקה דרך כל מיקום של step פיקסלים בפריים ומחזירה את ההתאמה הראשונה מעל הסף. image.SEARCH_DS הוא חיפוש היהלום: מנגנון ההתאמה דוגם תחילה בגסות, ואז מעדן סביב הניקוד הטוב ביותר, מה שמהיר בהרבה אך עלול לפספס התאמה אמיתית אם המעבר הגס במקרה נחת ליד מקסימום מקומי שעובר את המקסימום הגלובלי. עבור צינור עיבוד בזמן אמת שבו התבנית מוגדרת היטב ולא צפויה להתבלבל, SEARCH_DS הוא ברירת המחדל הנכונה; עבור כיול חד-פעמי שבו עלות הפספוס גבוהה יותר מעלות סריקה איטית יותר, SEARCH_EX בטוח יותר.
step שולט בדילוג הפיקסלים במהלך המעבר הממצה (חיפוש היהלום מנהל את הצעד שלו בעצמו). ערכי step גדולים יותר מאיצים את הסריקה במחיר של דיוק תת-פיקסלי. roi מגביל את החיפוש לאזור מסוים בפריים, גם מצמצם את מה שמנגנון ההתאמה שוקל וגם מפחית עבודה.
הערך המוחזר הוא טאפל של תיבה תוחמת (x, y, w, h) שמזהה את ההתאמה הטובה ביותר, או None אם אף מיקום לא עבר את הסף. התיבה התוחמת נכנסת ישירות לתוך draw_rectangle() או crop() עבור השלב הבא של העיבוד.
5.30.3. מלכודת קנה המידה והסיבוב¶
המכשול הקלאסי בהתאמת תבנית הוא רגישות לקנה מידה ולסיבוב. מנגנון ההתאמה משווה את התבנית מול הפריים פיקסל-לפיקסל; תבנית שנלכדה במרחק אחד אינה תואמת את אותו אובייקט שנלכד במרחק שונה, ותבנית שנלכדה ישירות מלפנים אינה תואמת את אותו אובייקט הנצפה מזווית. הסף יורד בשקט מתחת לרמת ההתאמה אפילו כשהאובייקט גלוי בבירור לעין אנושית, והשיטה מחזירה None.
קיימים כמה פתרונות עוקפים למקרים הפשוטים. היישום יכול ללכוד מספר תבניות בקני מידה שונים ולהריץ את find_template() עבור כל אחת ברצף, ולקבל את הראשונה שעוברת את הסף; העלות גדלה עם מספר התבניות. היישום יכול לעבד מראש את הפריים באמצעות rotation_corr() או טרנספורם הקוטב (Geometric transforms) כדי להסיר את הסיבוב המפריע לפני שההתאמה רצה; התבנית המותאמת עדיין צריכה להתאים לגאומטריה המתוקנת.
ניב שימושי עבור צינורות בקרת איכות מצמיד את מנגנון התאמת התבנית עם מחשבן הדמיון שניתוח טונאלי וסטטיסטי הציג: find_template() מאתרת את החלק בפריים שנלכד והתיבה התוחמת המוחזרת נחתכת ומועברת אל get_similarity() מול תבנית הייחוס. שלב התאמת התבנית מחליט היכן החלק נמצא; שלב ניקוד הדמיון מחליט האם החלק קביל. שני השלבים רצים בכל פריים, הסף על mean הוא שער עבר/נכשל, והתיבה התוחמת המותאמת המצוירת בחזרה אל הפריים היא התצוגה המקדימה ב-IDE שהמפעיל צופה בה.