7.3. Hello BlazeFace¶
BlazeFace היא רשת נוירונים לזיהוי פנים מאוסף MediaPipe של Google. קריאת הסקה אחת מחזירה מלבן תוחם סביב כל פנים שזוהו יחד עם שש נקודות ציון של הפנים – עין ימין, עין שמאל, אף, פה, אוזן ימין, אוזן שמאל. כל OpenMV Cam הנשלחת עם תמיכה ברשת נוירונים נושאת את מודל blazeface_front_128.tflite בזיכרון פלאש (flash), כך שהרצת מזהה פנים מקצה לקצה דורשת שורות ספורות של Python.
7.3.1. הסקריפט המלא¶
import csi
import ml
from ml.postprocessing.mediapipe import BlazeFace
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
while True:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
זהו מזהה הפנים בשלמותו. אין בו דבר מעבר לכך; הסקריפט לוכד פריים, מוסר אותו למודל, עובר על רשימת הזיהויים המוחזרת, ומצייר את המלבן התוחם של כל פנים יחד עם שש נקודות הציון שלהן בחזרה אל הפריים. התצוגה המקדימה ב-IDE מציגה את התיבות ואת נקודות הציון בזמן אמת.
7.3.2. מה כל שורה עושה¶
שלוש השורות הראשונות מייבאות את המודולים שהסקריפט זקוק להם. csi הוא ממשק חיישן המצלמה; ml הוא מודול הלמידה החישובית שעליו עוסק שאר הפרק; BlazeFace הוא המעבד-לאחר ההופך את טנזורי הפלט הגולמיים של BlazeFace אל רשימת התיבה התוחמת ונקודות הציון שהסקריפט עובר עליה.
חמש השורות הבאות מגדירות את החיישן. המצלמה מאופסת למצב ידוע, מוגדרת לצבע RGB565, מוגדרת לרזולוציית VGA, ולאחר מכן מחולנת לריבוע בגודל 400 על 400. החלון חשוב: BlazeFace אומנה על חיתוכים ריבועיים, ומתן קלט ריבועי מיישר את יחס הגובה-רוחב הצפוי של הרשת עם מה שהיא רואה בפריים הנלכד.
שורת טעינת המודל פותחת את קובץ המודל:
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
ml.Model קורא את הקובץ בנתיב הנתון – /rom/ היא מערכת קבצים השוכנת בזיכרון פלאש המכוסה בהמשך – ומחזיר אובייקט מודל שהסקריפט יריץ נגדו הסקות. מילת המפתח postprocess= רושמת את המעבד-לאחר של BlazeFace; בלעדיה, predict היה מחזיר את טנזורי הפלט הגולמיים של הרשת והאפליקציה הייתה צריכה לפענח אותם ידנית. עמה, predict מחזיר את התוצאה המפוענחת ישירות. הארגומנט threshold=0.4 במעבד-לאחר קובע את הביטחון המינימלי שהרשת חייבת לדווח עליו לפני ששומרים זיהוי; ערכים נמוכים יותר לוכדים פנים חלשות יותר במחיר של יותר זיהויי שווא.
ארבע השורות הנותרות הן הלולאה הראשית. כל מעבר דרכה לוכד פריים אחד ושואל את המודל מה הוא רואה:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
predict() מקבל רשימת קלטים (כאן, תמונה אחת שנלכדה) ומחזיר רשימת טאפלים של זיהויים. כל טאפל מחזיק את המלבן התוחם (x, y, w, h), ביטחון score בין אפס לאחד, ו-ndarray בגודל (6, 2) של קואורדינטות נקודות הציון – עין ימין, עין שמאל, אף, פה, אוזן ימין ואוזן שמאל באותו סדר. קריאת הציור משתמשת ב-draw_rectangle() – אותו פרימיטיב שבו הסתיים כל מזהה קלאסי בפרק התמונה – כדי לשרטט את קו המתאר של הפנים. ml.utils.draw_keypoints() היא פונקציית עזר קטנה מכלי ה-ml המסמנת כל נקודת מפתח בצלב במיקום ה-(x, y) שלה.
7.3.3. מה שהסקריפט אינו אומר¶
הסקריפט הוא שבע שורות ניתנות-הרצה של עבודת הסקה מעבר לייבואים ולהגדרת החיישן, אך כמות עצומה של חישוב מתרחשת בתוך אותן שבע שורות. הפריים RGB565 בגודל 400 על 400 שנלכד הופך לטנזור 8 סיביות מקוונטט בגודל 128 על 128 לפני שהוא מגיע לרשת; הרשת מריצה מאות פעולות כנגד עשרות אלפי משקלים; טנזורי ציוני הביטחון והיסטי התיבות הנובעים מכך הופכים לרשימה מדורגת של תיבות תוחמות לא-חופפות עם נקודות ציון מצורפות לפני ש-predict מחזיר. כל אחת מאותן טרנספורמציות היא משהו שהאפליקציה יכולה לשלוט בו אם היא צריכה, וכמה מהן חייבות להיות מכווננות עבור כל מודל שאינו ברירת מחדל.
ארבעת תתי-הסעיפים הבאים פותחים ומפרטים את אותן טרנספורמציות. לפי הסדר:
מודול ה-ml – מה ש-
ml.Modelחושף לאחר טעינת מודל, והיכן קובץ המודל שוכן בפועל על המצלמה.צינור ההסקה – ארבעת השלבים של כל קריאת
predict().מנועי הסקה – מסלולי ה-CPU וה-NPU המריצים את החישוב של הרשת.
פענוח הפלט – המעבדים-לאחר הממירים טנזורי פלט גולמיים אל הזיהויים שעליהם עבר הסקריפט הזה.
בסוף הפרק יוכל הקורא לכתוב את הסקריפט המקביל עבור מודל שלא נשלח עם המצלמה, לפענח טנזור שהמעבד-לאחר שלו עדיין אינו קיים, ולהסיק מדוע מודל מסוים רץ ב-30 FPS על מצלמה אחת וב-3 FPS על אחרת.