7.3. Hello BlazeFace

A BlazeFace egy arcészlelő neurális hálózat a Google MediaPipe gyűjteményéből. Egyetlen következtetési hívás minden észlelt arc köré egy határoló téglalapot ad vissza, hat arcponttal együtt – jobb szem, bal szem, orr, száj, jobb fül, bal fül. Minden OpenMV Cam, amely neurális hálózat támogatással érkezik, a flash memórián hordozza a blazeface_front_128.tflite modellt, így egy teljes körű arcészlelő futtatása néhány sor Pythonba kerül.

7.3.1. A teljes szkript

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

Ez a teljes arcészlelő. Nincs hozzá semmi más; a szkript elkap egy képkockát, átadja a modellnek, végigjárja a visszaadott észlelési listát, és minden arc határoló téglalapját, valamint a hat arcpontját visszarajzolja a képkockába. Az IDE előnézete valós időben mutatja a dobozokat és az arcpontokat.

7.3.2. Mit csinál az egyes sorok

Az első három sor importálja a szkripthez szükséges modulokat. A csi a kamera-érzékelő interfész; az ml a gépi tanulási modul, amelyről ez a fejezet szól; a BlazeFace az az utófeldolgozó, amely a BlazeFace nyers kimeneti tenzorait azzá a határoló doboz- és arcpontlistává alakítja, amelyen a szkript végigiterál.

A következő öt sor konfigurálja az érzékelőt. A kamera ismert állapotba kerül vissza, RGB565 színre van állítva, VGA felbontásra van állítva, majd egy 400×400-as négyzetre van ablakolva. Az ablak számít: a BlazeFace négyzet alakú kivágásokon lett betanítva, és egy négyzet alakú bemenet megadása összehangolja a hálózat által elvárt oldalarányt azzal, amit az elkapott képkockában lát.

A modellbetöltő sor megnyitja a modellfájlt:

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

A ml.Model beolvassa a fájlt a megadott útvonalon – a /rom/ egy flash memóriában rezidens fájlrendszer, amelyet később tárgyalunk –, és visszaad egy modellobjektumot, amelyen a szkript következtetéseket fog futtatni. A postprocess= kulcsszó regisztrálja a BlazeFace utófeldolgozót; nélküle a predict a hálózat nyers kimeneti tenzorait adná vissza, és az alkalmazásnak kézzel kellene dekódolnia azokat. Vele a predict közvetlenül a dekódolt eredményt adja vissza. Az utófeldolgozón lévő threshold=0.4 argumentum azt a minimális megbízhatóságot állítja be, amelyet a hálózatnak jelentenie kell, mielőtt egy észlelés megmaradna; az alacsonyabb értékek a halványabb arcokat is elkapják, több téves találat árán.

A maradék négy sor a fő ciklus. Minden áthaladás elkap egy képkockát, és megkérdezi a modellt, mit lát:

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

A predict() egy bemenetlistát fogad (itt egy elkapott képet), és egy észlelési sorrendezett listát ad vissza. Minden sorrendezett tartalmazza a határoló téglalapot (x, y, w, h), egy nulla és egy közötti megbízhatósági score-t, valamint egy (6, 2) méretű ndarray-t az arcpont-koordinátákkal – a jobb szem, a bal szem, az orr, a száj, a jobb fül és a bal fül ebben a sorrendben. A rajzolóhívás a draw_rectangle()-t használja – ugyanazt a primitívet, amellyel a képes fejezetben minden klasszikus észlelő végződött –, hogy körberajzolja az arcot. A ml.utils.draw_keypoints() egy kis segédfüggvény az ml segédeszközökből, amely minden kulcspontot egy kereszttel jelöl meg a (x, y) pozíciójában.

7.3.3. Amit a szkript nem mond el

A szkript hét futtatható sor következtetési munka az importokon és az érzékelő beállításán túl, de azon a hét soron belül rengeteg számítás történik. Az elkapott 400×400-as RGB565 képkocka egy 128×128-as kvantált 8 bites tenzorrá válik, mielőtt eléri a hálózatot; a hálózat több száz műveletet futtat le több tízezer súllyal szemben; a megbízhatósági pontszámokból és dobozeltolásokból álló tenzorok egy rangsorolt, nem átfedő határoló dobozok listájává válnak csatolt arcpontokkal, mielőtt a predict visszatér. Ezen átalakítások mindegyike olyasmi, amelyet az alkalmazás képes irányítani, ha szüksége van rá, és többüket bármely nem alapértelmezett modellhez be kell hangolni.

A következő négy alfejezet sorra kibontja ezeket az átalakításokat. Sorrendben:

  • Az ml modul – mit tesz elérhetővé a ml.Model, miután egy modell betöltődött, és hol él valójában a modellfájl a kamerán.

  • A következtetési folyamat – minden predict() hívás négy szakasza.

  • Következtetési motorok – a CPU- és NPU-utak, amelyek a hálózat aritmetikáját futtatják.

  • A kimenet dekódolása – az utófeldolgozók, amelyek a nyers kimeneti tenzorokat azokká az észlelésekké alakítják, amelyeken ez a szkript végigiterált.

A fejezet végére az olvasó meg tudja írni az egyenértékű szkriptet egy olyan modellhez, amely nem a kamerával együtt érkezett, dekódolni tud egy tenzort, amelynek az utófeldolgozója még nem létezik, és meg tudja indokolni, miért fut egy adott modell 30 FPS-sel az egyik kamerán és 3 FPS-sel egy másikon.