7.3. Hello BlazeFace¶
BlazeFace este o rețea neuronală de detectare a fețelor din colecția MediaPipe a Google. Un singur apel de inferență returnează un dreptunghi de încadrare în jurul fiecărei fețe detectate împreună cu șase repere faciale – ochiul drept, ochiul stâng, nasul, gura, urechea dreaptă, urechea stângă. Fiecare OpenMV Cam livrată cu suport pentru rețele neuronale poartă modelul blazeface_front_128.tflite în memoria flash, așa că rularea unui detector de fețe de la un capăt la altul necesită câteva linii de Python.
7.3.1. Scriptul complet¶
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))
Acela este întregul detector de fețe. Nu mai există nimic altceva; scriptul capturează un cadru, îl predă modelului, parcurge lista returnată de detectări și desenează dreptunghiul de încadrare al fiecărei fețe plus cele șase repere ale sale înapoi în cadru. Previzualizarea din IDE arată casetele și reperele în timp real.
7.3.2. Ce face fiecare linie¶
Primele trei linii importă modulele de care are nevoie scriptul. csi este interfața cu senzorul camerei; ml este modulul de învățare automată despre care tratează restul acestui capitol; BlazeFace este post-procesorul care transformă tensorii de ieșire bruți ai BlazeFace în lista de casete de încadrare și de repere pe care o iterează scriptul.
Următoarele cinci linii configurează senzorul. Camera este resetată la o stare cunoscută, setată la culoare RGB565, setată la rezoluție VGA, și apoi fereastruită la un pătrat de 400 pe 400. Fereastra contează: BlazeFace a fost antrenat pe decupaje pătrate, iar oferirea unei intrări pătrate aliniază raportul de aspect așteptat de rețea cu ceea ce vede în cadrul capturat.
Linia de încărcare a modelului deschide fișierul modelului:
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
ml.Model citește fișierul de la calea dată – /rom/ este un sistem de fișiere rezident în flash tratat mai târziu – și returnează un obiect model pe care scriptul va rula inferențe. Cuvântul-cheie postprocess= înregistrează post-procesorul BlazeFace; fără el, predict ar returna tensorii de ieșire bruți ai rețelei, iar aplicația ar trebui să îi decodeze manual. Cu el, predict returnează direct rezultatul decodat. Argumentul threshold=0.4 de pe post-procesor stabilește încrederea minimă pe care rețeaua trebuie să o raporteze înainte ca o detectare să fie păstrată; valorile mai mici captează fețe mai estompate cu prețul mai multor rezultate fals pozitive.
Celelalte patru linii sunt bucla principală. Fiecare trecere prin ea capturează un cadru și întreabă modelul ce vede:
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() ia o listă de intrări (aici, o imagine capturată) și returnează o listă de tuple de detectare. Fiecare tuplu conține dreptunghiul de încadrare (x, y, w, h), un scor de încredere score între zero și unu, și un ndarray (6, 2) de coordonate ale reperelor – ochiul drept, ochiul stâng, nasul, gura, urechea dreaptă și urechea stângă în această ordine. Apelul de desenare folosește draw_rectangle() – aceeași primitivă cu care s-a încheiat fiecare detector clasic din capitolul despre imagini – pentru a contura fața. ml.utils.draw_keypoints() este un mic ajutor din utilitarele ml care marchează fiecare punct-cheie cu o cruce la poziția sa (x, y).
7.3.3. Ce nu spune scriptul¶
Scriptul are șapte linii rulabile de lucru de inferență dincolo de importuri și de configurarea senzorului, dar o cantitate considerabilă de aritmetică se petrece în interiorul acestor șapte linii. Cadrul RGB565 capturat de 400 pe 400 devine un tensor cuantizat de 8 biți de 128 pe 128 înainte de a ajunge la rețea; rețeaua rulează sute de operații pe zeci de mii de ponderi; tensorii rezultați de scoruri de încredere și de deplasări ale casetelor devin o listă ordonată de casete de încadrare care nu se suprapun, cu repere atașate, înainte ca predict să returneze. Fiecare dintre aceste transformări este ceva ce aplicația poate controla dacă este nevoie, iar câteva dintre ele trebuie reglate pentru orice model care nu este cel implicit.
Următoarele patru subsecțiuni desfac aceste transformări. În ordine:
Modulul ml – ce expune
ml.Modelodată ce un model este încărcat, și unde trăiește efectiv fișierul modelului pe cameră.Conducta de inferență – cele patru etape ale fiecărui apel
predict().Motoarele de inferență – căile CPU și NPU care rulează aritmetica rețelei.
Decodarea ieșirii – post-procesoarele care convertesc tensorii de ieșire bruți în detectările pe care le-a iterat acest script.
Până la finalul capitolului cititorul poate scrie scriptul echivalent pentru un model care nu a fost livrat cu camera, poate decoda un tensor al cărui post-procesor nu există încă, și poate raționa despre motivul pentru care un anumit model rulează la 30 FPS pe o cameră și la 3 FPS pe alta.