7.3. Hello BlazeFace¶
BlazeFace on kasvojentunnistukseen tarkoitettu neuroverkko Googlen MediaPipe-kokoelmasta. Yksi päättelykutsu palauttaa rajaussuorakulmion jokaisen tunnistetun kasvon ympärille sekä kuusi kasvojen maamerkkiä – oikea silmä, vasen silmä, nenä, suu, oikea korva, vasen korva. Jokaisella OpenMV Camilla, joka toimitetaan neuroverkkotuen kanssa, on blazeface_front_128.tflite-malli flash-muistissa, joten päästä päähän toimivan kasvojentunnistimen ajaminen vaatii vain muutaman rivin Pythonia.
7.3.1. Koko skripti¶
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))
Siinä on koko kasvojentunnistin. Siihen ei kuulu mitään muuta; skripti kaappaa kehyksen, antaa sen mallille, käy läpi palautetun tunnistuslistan ja piirtää jokaisen kasvon rajaussuorakulmion sekä sen kuusi maamerkkiä takaisin kehykseen. IDE:n esikatselu näyttää laatikot ja maamerkit reaaliajassa.
7.3.2. Mitä kukin rivi tekee¶
Kolme ensimmäistä riviä tuovat skriptin tarvitsemat moduulit. csi on kamerasensorin rajapinta; ml on koneoppimismoduuli, josta tämän luvun loppuosa kertoo; BlazeFace on jälkikäsittelijä, joka muuntaa BlazeFacen raakatulostetensorit rajauslaatikko- ja maamerkkilistaksi, jota skripti käy läpi.
Seuraavat viisi riviä määrittävät sensorin. Kamera nollataan tunnettuun tilaan, asetetaan RGB565-väriin, asetetaan VGA-resoluutioon ja ikkunoidaan sitten 400 x 400 -neliöön. Ikkunalla on merkitystä: BlazeFace koulutettiin neliönmuotoisilla rajauksilla, ja neliönmuotoisen syötteen antaminen sille sovittaa verkon odottaman kuvasuhteen siihen, mitä se näkee kaapatussa kehyksessä.
Mallin latausrivi avaa mallitiedoston:
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
ml.Model lukee tiedoston annetusta polusta – /rom/ on flash-muistissa sijaitseva tiedostojärjestelmä, joka käsitellään myöhemmin – ja palauttaa malliobjektin, jota vasten skripti suorittaa päättelyjä. postprocess=-avainsana rekisteröi BlazeFace-jälkikäsittelijän; ilman sitä predict palauttaisi verkon raakatulostetensorit ja sovelluksen olisi purettava ne käsin. Sen kanssa predict palauttaa puretun tuloksen suoraan. Jälkikäsittelijän threshold=0.4-argumentti asettaa vähimmäisluottamuksen, joka verkon on raportoitava ennen kuin tunnistus säilytetään; pienemmät arvot tunnistavat himmeämpiä kasvoja useampien väärien positiivisten kustannuksella.
Loput neljä riviä ovat pääsilmukka. Jokainen kierros sen läpi kaappaa yhden kehyksen ja kysyy mallilta, mitä se näkee:
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() ottaa listan syötteitä (tässä yhden kaapatun kuvan) ja palauttaa listan tunnistustupleja. Jokainen tuple sisältää rajaussuorakulmion (x, y, w, h), luottamusarvon score nollan ja yhden välillä sekä (6, 2)-kokoisen ndarray:n maamerkkikoordinaatteja – oikea silmä, vasen silmä, nenä, suu, oikea korva ja vasen korva tässä järjestyksessä. Piirtokutsu käyttää draw_rectangle():tä – samaa primitiiviä, johon jokainen kuvaluvun klassinen tunnistin päättyi – kasvojen ääriviivan piirtämiseen. ml.utils.draw_keypoints() on pieni apuväline ml-työkaluista, joka merkitsee jokaisen avainpisteen ristillä sen (x, y)-kohtaan.
7.3.3. Mitä skripti ei kerro¶
Skripti on tuontien ja sensorin asetusten jälkeen seitsemän suoritettavaa riviä päättelytyötä, mutta noiden seitsemän rivin sisällä tapahtuu valtava määrä laskentaa. Kaapattu 400 x 400 RGB565 -kehys muuttuu 128 x 128 -kvantisoiduksi 8-bittiseksi tensoriksi ennen kuin se saapuu verkkoon; verkko suorittaa satoja operaatioita kymmeniätuhansia painoja vasten; tuloksena syntyvät luottamusarvojen ja laatikkosiirtymien tensorit muuttuvat järjestetyksi listaksi päällekkäin menemättömiä rajauslaatikoita liitettyine maamerkkeineen ennen kuin predict palaa. Jokainen näistä muunnoksista on jotain, mitä sovellus voi hallita tarvittaessa, ja useita niistä on viritettävä mille tahansa ei-oletusmallille.
Seuraavat neljä alalukua avaavat nämä muunnokset. Järjestyksessä:
ml-moduuli – mitä
ml.Modeltarjoaa, kun malli on ladattu, ja missä mallitiedosto todella sijaitsee kamerassa.Päättelyputki – jokaisen
predict()-kutsun neljä vaihetta.Päättelymoottorit – CPU- ja NPU-polut, jotka suorittavat verkon laskennan.
Tulosteen purkaminen – jälkikäsittelijät, jotka muuntavat raakatulostetensorit tunnistuksiksi, joita tämä skripti kävi läpi.
Luvun loppuun mennessä lukija osaa kirjoittaa vastaavan skriptin mallille, jota ei toimitettu kameran mukana, purkaa tensorin, jolle jälkikäsittelijää ei vielä ole, ja päätellä, miksi tietty malli toimii 30 FPS:n nopeudella yhdellä kameralla ja 3 FPS:n nopeudella toisella.