Ga naar de inhoud
BV Solution. Zelfstandig softwareontwikkelaar
EN NL
Laten we praten
AI-systemen

Gezichten tellen op groepsfoto’s, zonder ze naar een derde partij te sturen.

Een tourplatform moest de groepsgrootte kunnen controleren aan de hand van een foto. Zo kwam de detector in een kleine service naast de app terecht, en dit leerde ik eruit.

Abstracte illustratie van een groep grijze silhouetten, elk gemarkeerd met een groen detectiekader
Kort samengevat

Ik verving Google Vision door een kleine eigen service die gezichten telt voor een tourplatform. Ze telt enkel en herkent niemand, houdt de foto’s op de server en keurt nooit een check-in goed die ze niet kon controleren.

01 De opzet

Voor een tourorganisator bouwde ik een platform waarin gidsen hun groep inchecken door een foto te uploaden. De gids geeft op hoeveel volwassenen en kinderen er meegaan. Het systeem telt de gezichten op de foto en vergelijkt beide aantallen. Komen ze overeen, dan is de check-in rond. Komen ze niet overeen, dan bevestigt of past de gids het aantal aan, en een verschil wordt gemarkeerd zodat iemand het kan nakijken voor de commissie wordt uitbetaald.

Het systeem telt gezichten. Het herkent niemand. Dat was vanaf het eerste gesprek belangrijk, want op de foto’s staan gasten die nooit toestemden om geïdentificeerd te worden.

02 Waarom ik Google Vision liet vallen

Ik begon met Google Cloud Vision, maar gezichtsdetectie is niet beschikbaar op het Europese regionale endpoint. De foto’s zouden dus buiten de EU verwerkt worden, en bij foto’s van gasten wilde ik dat niet als standaard.

Daarom testte ik een open source detector, YuNet van OpenCV, op een reeks voorbeeldfoto’s van groepen. Hij haalde daar minstens dezelfde resultaten als Vision, draait op een gewone CPU, en de foto’s verlaten de server niet.

03 Een kleine service naast de app

YuNet draait via OpenCV, en dat is Python-terrein. Ik wilde geen PHP-extensie bevechten op een server waar die meestal niet staat. De detector zit daarom in een kleine FastAPI-service. Laravel stuurt de foto via HTTP door en krijgt een getal terug.

Laravel praat ermee via dezelfde interface als de Google Vision-versie. Het check-inscherm veranderde niet. De detector wisselen betekende één binding en één configuratiewaarde aanpassen.

Gids uploadt een groepsfoto Laravel-app
Foto via HTTP verstuurd localhost + gedeeld geheim
Service voor gezichtstelling FastAPI + YuNet
Getelde vs opgegeven aantallen gelijk: klaar · verschil: gemarkeerd voor review
De route van een check-infoto

04 Veilig laten draaien

Een service die foto’s aanvaardt, mag niet bereikbaar zijn voor wie toevallig de poort vindt. Dit zette ik eromheen:

  • In productie luistert ze enkel op localhost en ze hangt nooit aan de webserver.
  • Elke aanvraag heeft een gedeeld geheim van de Laravel-app nodig. Zonder dat antwoordt de service met 401.
  • Bij het opstarten wordt het modelbestand vergeleken met een opgeslagen checksum. Klopt die niet, dan weigert de service te tellen.
  • Uploads hebben een groottelimiet, zodat één enorm bestand de CPU niet kan blokkeren.

05 Gaat het mis, dan zie je dat

Is de service uit de lucht, traag of overbelast, dan krijgt de gids een foutmelding. Het systeem telt nooit een overeenkomst die het niet gecontroleerd heeft. De commissie hangt af van dit getal, dus stilzwijgend goedkeuren zou het doel tenietdoen. Een manuele melding blijft beschikbaar als terugvaloptie.

Geen gecontroleerde overeenkomst, geen goedkeuring.

06 Wat een belastingstest me leerde

Ik schreef een klein stressscript dat veel check-ins tegelijk naar de service stuurt, zoals wanneer veel gidsen hun tour op hetzelfde moment afronden. Mijn eerste idee was meer worker-processen starten. Met de standaardinstellingen werd het daardoor trager: elke worker startte ook een hoop threads, en die vochten om dezelfde CPU-kernen.

Eén detectiethread per worker, en het aantal processen laten bepalen hoeveel er parallel loopt, loste het op. Dat had ik niet gevonden door code te lezen. Ik had de test nodig.

07 Wat het niet kan

Een drukke foto met weggedraaide gezichten geeft een te lage telling, en het systeem vergelijkt exacte aantallen. Ik voegde geen marge toe, want een gemarkeerd geval vind ik beter dan een verborgen marge. De gids krijgt op de fotostap een korte tip om de gasten naar de camera te laten kijken, en een verschil is een normale uitkomst met een normale afhandeling.

08 Denk je aan iets gelijkaardigs?

Bij AI in een bedrijfsproces geeft het model een antwoord, en het proces moet beslissen wat er gebeurt als dat antwoord fout is of ontbreekt. Ontwerp dat deel eerst. Het model is zelden het moeilijke stuk.

Volgende stap

Wil je AI in een echte workflow?

Vertel wat je wil bouwen, automatiseren of oplossen. Een paar regels is genoeg.

Ik lees alles zelf. Geen nieuwsbrief, geen CRM-reeks.