7.3. Hello BlazeFace

BlazeFace je neuronska mreža za detekciju lica iz Googleove zbirke MediaPipe. Jedan poziv zaključivanja vraća granični pravokutnik oko svakog detektiranog lica zajedno sa šest orijentira lica – desno oko, lijevo oko, nos, usta, desno uho, lijevo uho. Svaki OpenMV Cam koji se isporučuje s podrškom za neuronske mreže nosi model blazeface_front_128.tflite na flash memoriji, pa pokretanje cjelovitog detektora lica zahtijeva svega nekoliko redaka Pythona.

7.3.1. Cijela skripta

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))

To je cijeli detektor lica. Nema ničeg drugog u njemu; skripta hvata sličicu, predaje je modelu, prolazi vraćeni popis detekcija i crta granični pravokutnik svakog lica te njegovih šest orijentira natrag u sličicu. Pretpregled u IDE-u u stvarnom vremenu prikazuje okvire i orijentire.

7.3.2. Što svaki redak radi

Prva tri retka uvoze module koje skripta treba. csi je sučelje senzora kamere; ml je modul za strojno učenje o kojem govori ostatak ovog poglavlja; BlazeFace je naknadni obrađivač koji BlazeFaceove sirove izlazne tenzore pretvara u popis graničnih okvira i orijentira kroz koji skripta iterira.

Sljedećih pet redaka konfigurira senzor. Kamera se resetira u poznato stanje, postavlja na RGB565 boju, postavlja na VGA razlučivost i zatim prozoruje na kvadrat 400 puta 400. Prozor je važan: BlazeFace je treniran na kvadratnim izrezima, a davanje kvadratnog ulaza poravnava mrežin očekivani omjer širine i visine s onim što vidi u uhvaćenoj sličici.

Redak za učitavanje modela otvara datoteku modela:

model = ml.Model("/rom/blazeface_front_128.tflite",
                 postprocess=BlazeFace(threshold=0.4))

ml.Model čita datoteku na danoj putanji – /rom/ je datotečni sustav smješten u flashu, obrađen kasnije – i vraća objekt modela nad kojim će skripta pokretati zaključivanja. Ključna riječ postprocess= registrira BlazeFace naknadni obrađivač; bez nje, predict bi vratio mrežine sirove izlazne tenzore i aplikacija bi ih morala ručno dekodirati. S njom, predict izravno vraća dekodirani rezultat. Argument threshold=0.4 na naknadnom obrađivaču postavlja minimalnu pouzdanost koju mreža mora prijaviti prije nego se detekcija zadrži; niže vrijednosti hvataju slabija lica po cijenu više lažno pozitivnih.

Preostala četiri retka su glavna petlja. Svaki prolaz kroz nju hvata jednu sličicu i pita model što vidi:

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() uzima popis ulaza (ovdje, jednu uhvaćenu sliku) i vraća popis n-torki detekcija. Svaka n-torka drži granični pravokutnik (x, y, w, h), pouzdanost score između nule i jedan, te (6, 2) ndarray koordinata orijentira – desno oko, lijevo oko, nos, usta, desno uho i lijevo uho tim redoslijedom. Poziv za crtanje koristi draw_rectangle() – isti primitiv kojim je završavao svaki klasični detektor u poglavlju o slici – za ocrtavanje lica. ml.utils.draw_keypoints() je mali pomoćnik iz ml uslužnih alata koji svaku ključnu točku označava križićem na njezinoj (x, y) poziciji.

7.3.3. Što skripta ne kaže

Skripta je sedam izvedivih redaka zaključivanja nakon uvoza i postavljanja senzora, no unutar tih sedam redaka odvija se velika količina aritmetike. Uhvaćena RGB565 sličica 400 puta 400 postaje kvantizirani 8-bitni tenzor 128 puta 128 prije nego dođe do mreže; mreža izvodi stotine operacija nad desecima tisuća težina; nastali tenzori pouzdanosti i pomaka okvira postaju rangirani popis nepreklapajućih graničnih okvira s pridruženim orijentirima prije nego predict vrati. Svaka od tih transformacija nešto je što aplikacija može kontrolirati ako treba, a nekoliko ih se mora ugoditi za bilo koji nezadani model.

Sljedeća četiri pododjeljka razlažu te transformacije. Redom:

  • The ml module – što ml.Model izlaže kada se model učita, i gdje datoteka modela zapravo živi na kameri.

  • The inference pipeline – četiri stadija svakog predict() poziva.

  • Inference engines – CPU i NPU putovi koji izvode mrežinu aritmetiku.

  • Decoding the output – naknadni obrađivači koji sirove izlazne tenzore pretvaraju u detekcije kroz koje je ova skripta iterirala.

Do kraja poglavlja čitatelj može napisati ekvivalentnu skriptu za model koji nije isporučen s kamerom, dekodirati tenzor čiji naknadni obrađivač još ne postoji, i razumjeti zašto određeni model radi pri 30 FPS na jednoj kameri, a 3 FPS na drugoj.