Gesichter und Gesichtsmerkmale in Echtzeit erkennen

Willkommen zu einem weiteren Artikel, der Apples Vision- und VisionKit-Frameworks erkundet. Ich habe bereits zwei Artikel und Code-Beispiele geschrieben, die diese Frameworks verwenden.
Der erste, den ich frueher geschrieben habe, ist “Barcode-Scanner in SwiftUI”.
Der zweite Artikel ist ganz neu und heisst “Dokumentenscanner in SwiftUI”.
Jetzt ist es Zeit fuer etwas Neues und noch Spassigeres.
Einfuehrung
Die vorhandenen Frameworks fuer Gesichtserkennung und Gesichtsmerkmal-Erkennung zu erkunden, ist etwas, das ich schon lange tun wollte.
Ich finde es faszinierend, dass clevere Algorithmen Gesichter erkennen und verfolgen koennen, selbst wenn Personen Brillen oder Masken tragen.
Dank Apple koennen wir all diese Funktionalitaet direkt auf unseren Telefonen haben. Nichts muss zur Verarbeitung an Server gesendet werden - das waere ohnehin zu langsam.
Wir wollen alles in Echtzeit, direkt auf unserem Geraet.
Daher habe ich dieses Projekt gestartet, das in diesem Artikel und einer voll funktionsfaehigen iOS-App resultierte. Du kannst das begleitende Xcode-Projekt herunterladen und den Quellcode frei in deinen eigenen Projekten verwenden.
Lass uns zuerst darueber sprechen, was die App tatsaechlich macht.
App-Funktionen
Der Titel sagt bereits, dass es in diesem Artikel um Gesichtserkennung und Gesichtsmerkmal-Erkennung geht. Die App macht aber ein bisschen mehr.
Apples Vision- und VisionKit-Frameworks liefern die Algorithmen fertig, und ich habe die folgenden Funktionen in die Beispiel-App aufgenommen:
- Bounding Box erkennen und visualisieren
- Gesichtsmerkmale erkennen und visualisieren
- Bildaufnahmequalitaet bestimmen
- Kopfposition bestimmen
Der vollstaendige Xcode-Workspace mit der voll funktionsfaehigen App kann hier heruntergeladen werden.
So sieht die App in Aktion aus:
Die App wurde in Xcode 13.2.1, Swift Language Version 5 geschrieben und auf einem iPhone 11 Pro Max mit iOS 15.2.1 getestet.
Im naechsten Abschnitt erklaere ich die Funktionen genauer.
Was ist eine Bounding Box?
Wenn wir das Gesicht einer Person als Ganzes verfolgen wollen, verwenden wir die Bounding Box. Wir erhalten diese Werte direkt von den Erkennungsalgorithmen und koennen sie verwenden, um zum Beispiel ein gruenes Quadrat um das Gesicht zu visualisieren.
Genau das macht die Beispiel-App. Du kannst die Visualisierung der Bounding Box an deinen Anwendungsfall anpassen. Vielleicht brauchst du eine andere Farbe oder gestrichelte Linien statt durchgezogener? Das ist eine einfache Aufgabe mit SwiftUI und der Art, wie ich die Erkennungslogik von ihrer Visualisierung entkoppelt habe.

Was sind Gesichtsmerkmale?
Anstatt das ganze Gesicht als eine Einheit zu betrachten, geben uns Gesichtsmerkmale mehr Details zu spezifischen Gesichtsmerkmalen. Was wir von den Erkennungsalgorithmen erhalten, sind Saetze von Koordinaten, die Merkmale wie Mund, Nase, Augen und so weiter darstellen.

Was ist Aufnahmequalitaet?
Der Aufnahmequalitaets-Indikator liefert einen spezifischen Wert, der angibt, wie gut das Bild fuer die Erkennung ist. Je hoeher der Wert, desto besser die Qualitaet. Der Bereich ist von 0.0 bis 1.0.
Das ist besonders nuetzlich, wenn du eine Reihe von Bildern desselben Subjekts hast, z.B. um die beste Aufnahme fuer die weitere Verarbeitung auszuwaehlen.

Was sind die verschiedenen Kopfpositionen?
Eine weitere faszinierende Metrik ist die Kopfposition. Tatsaechlich bekommen wir drei Metriken: Roll, Yaw und Pitch. Das Bild unten zeigt die Unterschiede zwischen diesen Werten.

Jetzt, da wir wissen, was die Beispiel-App macht, werden wir lernen, wie sie organisiert ist, um diese Funktionen zu implementieren.
App-Architektur und Code-Organisation
Bei der Recherche fuer diesen Artikel bin ich auf viele Beispiele gestossen. Fast alle hatten dasselbe Problem: das “Massive View Controller”-Antimuster.
Auf hoher Ebene hat die Beispiel-App drei Verarbeitungsschritte:
- Eine Bildsequenz aufnehmen
- Die Erkennungsalgorithmen ausfuehren
- Das Ergebnis visualisieren
In einer Anwendung im “Massive View Controller”-Stil werden fast alle diese Schritte von einer riesigen Klasse implementiert. In klassischen UIKit-Apps wird das normalerweise in UIViewControllers gemacht - daher der Name. War dort, hab das gemacht.
Was wir stattdessen wollen, ist gutes Code-Design in Form von “Separation of Concerns”, wo jede Klasse einem spezifischen Zweck dient. Wir koennen auch auf die SOLID-Prinzipien von Uncle Bob (Robert C. Martin) schauen. Das, was wir hier wollen, ist das “Single-Responsibility-Prinzip”.
Daher war mein Hauptziel fuer dieses Projekt, eine klare und praezise Codestruktur zu haben, indem die Belange von Aufnahme, Erkennung und Visualisierung in getrennte Klassen aufgeteilt und sie ueber einen Pipeline-Mechanismus verbunden werden.
Das Projekt in Xcode ist wie folgt strukturiert:

FaceDetectorApp: Der Einstiegspunkt der Anwendung, der den Application Delegate haelt. Der Application Delegate ist der Ort, an dem wir die verschiedenen funktionalen Komponenten instanziieren und verbinden.ContentView: Die oberste View fuer die Anwendung.CameraView: Es gibt noch keine native Kameraansicht fuer SwiftUI. Daher brauchen wir diese Hilfsklasse, um die Video-Vorschauebene zu wrappen, die nativ fuer UIKit ist.CaptureSession: Diese Klasse ist verantwortlich fuer das Aufnehmen der Bildsequenz (des Video-Feeds).FaceDetector: Hier passiert die Magie! Alle Erkennungsalgorithmen werden von dieser Klasse aufgerufen.AVCaptureVideoOrientation: Eine Hilfsfunktion zur Konvertierung vonUIDeviceOrientationzuAVCaptureVideoOrientation, die fuer die korrekte Visualisierung benoetigt wird.
Die Klassen allein sind nicht wirklich nuetzlich. Daher muessen sie verbunden werden. Ich bevorzuge das Pipeline-Muster. Ich liebe es, Code zu schreiben, indem ich die Elemente einer Pipeline implementiere und sie dann in einem deklarativen Stil verbinde. Deshalb habe ich Apples Einfuehrung des Combine-Frameworks begruest. Ich verwende es dort, wo es Sinn macht und nicht mit der Klarheit und Verstaendlichkeit des Codes interferiert.
Das Bild unten zeigt die Elemente unserer Pipeline und ihre Ein- und Ausgabedatentypen. Alle Ausgabevariablen werden mit Combine-Publishern (@Published) realisiert. Das naechste Pipeline-Element abonniert diesen Publisher.

Das war’s in etwa darueber, wie die Beispielanwendung strukturiert ist, um unsere erforderlichen Funktionen zu implementieren.
Es ist jetzt Zeit, alles zusammenzufassen.
Fazit
Dieses Tutorial sollte dir einen tiefgehenden Einblick gegeben haben, was noetig ist, um Gesichts- und Gesichtsmerkmal-Erkennung zu implementieren.
Nachdem ich erklaert habe, welche Art von Informationen wir von Apples Algorithmen sammeln koennen, habe ich detailliert dargelegt, wie ich den Code der Beispielanwendung strukturiert habe.
Mein Hauptziel war es, ein sauberes Code-Design zu liefern, um den Erkennungscode in eine leicht verstaendliche und wiederverwendbare Pipeline zu entwirren.
Der vollstaendige Xcode-Workspace mit der voll funktionsfaehigen App kann hier heruntergeladen werden.
Danke fuers Lesen!