7.3. Hello BlazeFace

BlazeFace is een neuraal netwerk voor gezichtsdetectie uit Googles MediaPipe-collectie. Eén inferentie-aanroep retourneert een begrenzingsrechthoek rond elk gedetecteerd gezicht samen met zes gezichtslandmarks – rechteroog, linkeroog, neus, mond, rechteroor, linkeroor. Elke OpenMV Cam die met ondersteuning voor neurale netwerken wordt uitgeleverd draagt het blazeface_front_128.tflite-model in het flashgeheugen, dus het draaien van een end-to-end gezichtsdetector kost een paar regels Python.

7.3.1. Het volledige script

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

Dat is de volledige gezichtsdetector. Er zit niets anders aan; het script legt een frame vast, geeft het aan het model door, doorloopt de geretourneerde lijst met detecties en tekent de begrenzingsrechthoek van elk gezicht plus zijn zes landmarks terug in het frame. De preview in de IDE toont de vakken en landmarks in realtime.

7.3.2. Wat elke regel doet

De eerste drie regels importeren de modules die het script nodig heeft. csi is de interface voor de camerasensor; ml is de machine-learning-module waar de rest van dit hoofdstuk over gaat; BlazeFace is de post-processor die de ruwe uitvoertensoren van BlazeFace omzet in de begrenzingsvak- en landmarklijst die het script doorloopt.

De volgende vijf regels configureren de sensor. De camera wordt teruggezet naar een bekende toestand, ingesteld op RGB565-kleur, ingesteld op VGA-resolutie, en vervolgens gewindowd naar een vierkant van 400 bij 400. Het venster doet ertoe: BlazeFace is getraind op vierkante uitsnedes, en door het een vierkante invoer te geven komt de verwachte beeldverhouding van het netwerk overeen met wat het in het vastgelegde frame ziet.

De regel die het model laadt opent het modelbestand:

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

ml.Model leest het bestand op het opgegeven pad – /rom/ is een in het flashgeheugen residerend bestandssysteem dat later wordt behandeld – en retourneert een modelobject waartegen het script inferenties zal draaien. Het postprocess=-keyword registreert de BlazeFace-post-processor; zonder dat zou predict de ruwe uitvoertensoren van het netwerk retourneren en zou de applicatie ze met de hand moeten decoderen. Met dat keyword retourneert predict het gedecodeerde resultaat direct. Het threshold=0.4-argument op de post-processor stelt de minimale betrouwbaarheid in die het netwerk moet rapporteren voordat een detectie wordt behouden; lagere waarden vangen zwakkere gezichten op ten koste van meer valse positieven.

De resterende vier regels vormen de hoofdlus. Bij elke doorgang wordt één frame vastgelegd en wordt het model gevraagd wat het ziet:

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() neemt een lijst met invoeren (hier één vastgelegde afbeelding) en retourneert een lijst met detectie-tuples. Elke tuple bevat de begrenzingsrechthoek (x, y, w, h), een betrouwbaarheids-score tussen nul en één, en een (6, 2) ndarray met landmarkcoördinaten – het rechteroog, linkeroog, neus, mond, rechteroor en linkeroor in die volgorde. De tekenaanroep gebruikt draw_rectangle() – dezelfde primitief waarmee elke klassieke detector in het beeldhoofdstuk eindigde – om het gezicht te omlijnen. ml.utils.draw_keypoints() is een kleine helper uit de ml-utilities die elk sleutelpunt markeert met een kruisje op zijn (x, y)-positie.

7.3.3. Wat het script niet zegt

Het script bestaat na de imports en de sensoropzet uit zeven uitvoerbare regels inferentiewerk, maar er gebeurt een aanzienlijke hoeveelheid rekenwerk binnen die zeven regels. Het vastgelegde RGB565-frame van 400 bij 400 wordt een gekwantiseerde 8-bits tensor van 128 bij 128 voordat het het netwerk bereikt; het netwerk voert honderden bewerkingen uit tegen tienduizenden gewichten; de resulterende tensoren met betrouwbaarheidsscores en box-offsets worden een gerangschikte lijst van niet-overlappende begrenzingsvakken met bijbehorende landmarks voordat predict retourneert. Elk van die transformaties is iets dat de applicatie kan aansturen als dat nodig is, en verschillende ervan moeten worden afgesteld voor elk niet-standaardmodel.

De volgende vier subsecties openen die transformaties. In volgorde:

  • De ml-module – wat ml.Model blootstelt zodra een model is geladen, en waar het modelbestand daadwerkelijk op de cam leeft.

  • De inferentiepijplijn – de vier fasen van elke predict()-aanroep.

  • Inferentie-engines – de CPU- en NPU-paden die het rekenwerk van het netwerk draaien.

  • De uitvoer decoderen – de post-processors die ruwe uitvoertensoren omzetten in de detecties die dit script doorliep.

Aan het einde van het hoofdstuk kan de lezer het equivalente script schrijven voor een model dat niet met de cam werd uitgeleverd, een tensor decoderen waarvan de post-processor nog niet bestaat, en redeneren over waarom een bepaald model op de ene cam met 30 FPS draait en op een andere met 3 FPS.