5.25. מציאת רכיבים (blobs)

הספירה (thresholding) הפכה את הפריים שנלכד למסכה בינארית: כל פיקסל או עובר את מבחן הסף או לא. זה עונה על השאלה אילו צבעים שהאפליקציה מתעניינת בהם מופיעים בסצנה, אך לא היכן – המסכה היא רק ים של 1-ים ו-0-ים. השלב הבא הוא זיהוי רכיבים: סריקת המסכה, מציאת אזורים רציפים של פיקסלים עוברים, והחזרת כל אחד מהם כאובייקט בעל מיקום, גודל, אוריינטציה, ושאר התכונות שאפליקציה יכולה לפעול על פיהן.

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

5.25.1. הקריאה הבסיסית

find_blobs מקבלת רשימת ספים ומחזירה רשימת אובייקטי תוצאה מסוג blob:

thresholds = [(30, 100, 15, 127, 15, 127)]  # LAB threshold for red
blobs = img.find_blobs(thresholds)

for b in blobs:
    img.draw_rectangle(b.rect, color=(255, 0, 0))
    img.draw_cross(b.cx, b.cy, color=(255, 0, 0))

לכל טופל סף יש אותה צורה כמו לספים המועברים אל binary() – שישה ערכים (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) עבור תמונת RGB565 (הגבולות הם ב-LAB), שני ערכים (lo, hi) עבור תמונה בגווני אפור. ניתן לספק עד 32 ספים בקריאה אחת, וזה מה שהופך את find_blobs() לכה גמישה: ניתן לעקוב במקביל אחר משואות אדומות, ירוקות וכחולות, כשכל אחת תורמת את ה-blobs שלה לרשימה המוחזרת, ותכונת ה-code של כל blob מזהה איזה סף הוא תאם.

הקריאות draw_rectangle() ו-draw_cross() לעיל מסמנות את הפריים שנלכד עבור התצוגה המקדימה ב-IDE. תוצאת ה-blob כבר נושאת את b.rect (התיבה התוחמת כטופל בן 4) ואת b.cx / b.cy (הצנטרואיד השלם), כך שציור הזיהוי בחזרה אל תוך הפריים הוא שתי קריאות שיטה.

5.25.2. מה מכילה התוצאה

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

קבוצת התיבה התוחמת והצנטרואידx, y, w, h, rect, cx, cy, cxf, cyf – מתארת את מיקום ה-blob. rect הוא הטופל בן 4 (x, y, w, h) ששיטות הציור מצפות לו; cx ו-cy הם הצנטרואיד בקואורדינטות פיקסל שלמות; cxf ו-cyf הם הצנטרואיד בקואורדינטות נקודה צפה תת-פיקסליות, שימושיות כאשר כיול במעלה הזרם מתעניין במיקומים שבריריים.

מתארי הצורהpixels, area, density, perimeter, roundness, elongation, compactness, rotation – מתארים כיצד ה-blob נראה. pixels הוא מניין הפיקסלים העוברים; area הוא שטח התיבה התוחמת מיושרת-הצירים (w * h); density הוא היחס בין השניים, המתקרב אל 1.0 עבור מלבן מלא ויורד לעבר 0.0 עבור משיכה אלכסונית דקה. roundness ו-compactness שניהם מנקדים עד כמה ה-blob עגול, מנקודות מבט גאומטריות שונות (roundness מהמומנטים מסדר שני, compactness מיחס ההיקף-לשטח); elongation הוא 1.0 - roundness לנוחות. rotation הוא האוריינטציה של הציר הראשי ברדיאנים, המדויקת ביותר על blobs מוארכים והופכת רועשת על כאלה כמעט-עגולים (לציר עמום אין כיוון מוגדר היטב).

מטא-נתוני הסף והמיזוגcode, count – מזהים איזה סף תאם וכמה blobs מקור מוזגו אל זה המוחזר. code הוא מפת ביטים בת 32 ביט עם ביט אחד מודלק לכל סף תואם (סף יחיד נותן code == 1; blob ממוזג רב-צבעוני יכול להחזיק כמה ביטים מודלקים); count הוא 1 אלא אם merge=True שילב כמה זיהויים לאחד.

קבוצת הפינותcorners, min_corners – נותנת את הגאומטריה המסובבת של ה-blob. corners הוא הטופל בן 4 של קיצוני (x, y) הנמשכים מקו המתאר של ה-blob, ממוין בכיוון השעון מהפינה השמאלית-עליונה; min_corners הוא הטופל בן 4 של פינות עבור המלבן המסובב מינימלי-השטח התוחם את ה-blob. המלבן מינימלי-השטח הוא ההתאמה ההדוקה; ה-rect מיושר-הצירים הוא ההתאמה הרופפת המיושרת עם רשת הפיקסלים. שניהם שימושיים, תלוי האם שלב במורד הזרם זקוק לתיבה מכוונת או לתיבה רגילה.

A blob detection illustrated against a binary threshold mask. The left panel shows a tilted oval mask of passing pixels. The right panel shows the same mask annotated with the axis-aligned bounding box drawn around it, the centroid marked with a cross in the middle, a dashed minimum-area rotated rectangle hugging the oval at its true angle, and the major-axis line through the centroid pointing along the oval's long direction.

blob נושא את התיבה התוחמת מיושרת-הצירים (rect, x, y, w, h), את הצנטרואיד (cx, cy או התת-פיקסליים cxf, cyf), את המלבן המסובב מינימלי-השטח (min_corners בתוספת rotation), ואת קווי הציר הראשי / משני האופציונליים המחושבים על-ידי פונקציות העזר ברמת המודול שלהלן.

5.25.4. מיזוג blobs חופפים

merge=True מעבד-אחר את רשימת התוצאה כדי לשלב blobs שמלבניהם התוחמים חופפים. השימוש הטבעי הוא זיהוי מטרה שצבעה נראה למצלמה כמספר אזורים מסופרים בשל בוהקים ספקולריים, קווי צל, או תאורה לא תואמת על פני האובייקט: כדור אדום יחיד עשוי לחזור כשלושה או ארבעה blobs אדומים קטנים אשר, יחד, מתווים את הכדור. עם merge=True, שלושת ה-blobs הופכים ל-blob גדול אחד, ה-rect מכסה את האיחוד, ה-code הוא OR ביטי של הקודים של ה-blobs הממוזגים (כך שמיזוג רב-צבעוני מזהה אילו צבעים תרמו), ו-count מדווח כמה blobs מקור שולבו.

margin מגדיל או מקטין את המלבנים התוחמים לפני מבחן החפיפה. עם margin=2, blobs שמלבניהם התוחמים מגיעים עד 2 פיקסלים זה מזה עדיין מתמזגים; עם margin=-2, רק blobs שמלבניהם התוחמים חופפים בלפחות 2 פיקסלים מתמזגים. הכוונון הטבעי: margin חיובי כדי לטפל ב-blobs שהסף שבר לחתיכות סמוכות; margin שלילי כדי לשמור אובייקטים נפרדים המקובצים בצפיפות נפרדים.

merge_cb רץ על כל זוג מועמד לפני שהמיזוג מתרחש. ה-callback מקבל את שני ה-blobs ומחזיר True כדי לאפשר את המיזוג או False כדי למנוע אותו. זהו הכלי הנכון לבדיקה צולבת של מיזוגים שהכלל הגאומטרי מפספס – למשל, סירוב למזג שני blobs שזוויות ה-rotation שלהם נבדלות ביותר מסף, או סירוב למזג blob קטן לתוך גדול בהרבה אם הקטן הוא רק נקודות רעש.

5.25.5. היסטוגרמות היטל

x_hist_bins_max ו-y_hist_bins_max מצרפים היסטוגרמות היטל אופציונליות לכל blob. היסטוגרמת היטל היא מניין הפיקסלים העוברים לאורך ציר אחד: היסטוגרמת ציר ה-X מסכמת פיקסלים עוברים לכל עמודה בתוך התיבה התוחמת של ה-blob, והיסטוגרמת ציר ה-Y מסכמת לכל שורה. שתיהן בברירת מחדל אפס – ההיסטוגרמות אינן מחושבות אלא אם סופק max שונה מאפס, מאחר שאחרת היו מוסיפות עבודה לכל זיהוי.

כשהן מחושבות, ההיסטוגרמות מספקות אות חד-ממדי זול שאפליקציה יכולה להריץ עליו ניתוח נוסף: זיהוי מיקום של פס אנכי בתוך ה-blob, מציאת נקודת השבירה של מטרה דו-צבעונית, ספירת כמה פערים מופיעים לאורך הציר הארוך. הן ממולאות כתכונות x_hist_bins ו-y_hist_bins על כל Blob.

5.25.6. פונקציות עזר גאומטריות נוספות

מקבץ של מדדים גאומטריים נוספים חי כפונקציות ברמת המודול המקבלות blob ומחזירות את המדידה המבוקשת:

  • image.get_solidity() מחזירה את ה-solidity של ה-blob – פיקסלים מחולק בשטח המעטפת הקמורה. אזור מלא ומוצק קרוב ל-1.0; blob בעל קעירויות (פרסה, יד עם אצבעות פרושות) יורד הרבה מתחת.

  • image.get_convexity() מחזירה את ה-convexity – היקף המעטפת הקמורה מחולק בהיקף ה-blob. blob קמור באופן מושלם הוא 1.0; blobs משוננים או חרוצים נמוכים יותר.

  • image.get_major_axis_line() ו-image.get_minor_axis_line() מחזירות אובייקטי Line לאורך הציר הראשי והמשני של ה-blob, הנגזרים מהמלבן המסובב מינימלי-השטח.

  • image.get_enclosing_circle() מחזירה Circle התוחם את ה-blob – שימושי כאשר שלב במורד הזרם רוצה מעגל לציור או לבדיקה כנגדו.

  • image.get_enclosed_ellipse() מחזירה את הטופל בן 5 (cx, cy, rx, ry, rotation) עבור אליפסה חסומה במלבן מינימלי-השטח של ה-blob. הערכים מוזנים ישירות אל draw_ellipse().

5.25.7. למידה אוטומטית של סף

מזהה blob טוב רק כמו הספים שאיתם הוא רץ, והעבודה של מציאת הסף הנכון לצבע מטרה היא בעיה בפני עצמה. שתי תבניות נפוצות מצמצמות את העבודה הזו.

הראשונה היא בחירה אינטראקטיבית ב-IDE: לכוד פריים, גרור מלבן סביב דוגמה של צבע המטרה, ותן ל-עורך הסף של ה-IDE לדווח על גבולות ה-LAB שהוא רואה. גבולות אלה נופלים אל הסקריפט כספים של find_blobs() והמזהה מוכן.

השנייה היא למידה-אוטומטית תוכנתית: שגרת כיול הרצה על המצלמה לוכדת פריים, לוקחת היסטוגרמה של מתחם ידוע שבו נמצאת המטרה (get_histogram() עם roi=), וקוראת את טווח הערכים של המתחם מתוך ההיסטוגרמה באמצעות get_percentile(). האחוזון ה-5 קובע את הגבול התחתון של כל ערוץ וה-95 את הגבול העליון, תוך התעלמות מפיקסלים חריגים תועים בשני הקצוות. בתמונת RGB565 קריאת אחוזון אחת מדווחת על כל שלושת ערוצי ה-LAB בבת אחת, כך ששתי הקריאות מפיקות את ששת המספרים ש-find_blobs() מצפה להם:

h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
             lo.a_value, hi.a_value,
             lo.b_value, hi.b_value)