Haarerkennung mit der Kinect

Gerade beschäftige ich mich mit Bilderkennung, genauer gesagt mit dem Erkennen von Personen und deren Eigenschaften (“Features”). Dabei ist wenig vorgegeben, aber man landet recht schnell bei zwei Kandidaten, je nach Anwendungsfall:

  1. Bei der Kinect für Bewegungs- (Gesten-, …), Tiefen- und Gesichtserkennung
  2. Bei OpenCV für “Feature Detection” im eigentlichen Sinne, also weniger “wo ist das Gesicht” oder “wie steht der Arm”, als “trägt das Gesicht eine Brille” oder “wie viele Finger zeigt die Hand”

Zwar bringt die Kinect ebenfalls die Erkennung bestimmter Features mit, OpenCV funktioniert nur halt allgemeiner. In erster Annährung habe ich mich trotzdem auf die Kinect konzentriert, damit geht insbesondere im Gesicht schon recht viel (und ich muss nur eine neue Technologie zur Zeit lernen):

Direktlink, Quelle

Knifflig wird es, wenn man mehr als “nur” das Gesicht erkennen will – beispielsweise den Kopf als Ganzes. Oder die Frisur. Oder auch nur die Haarfarbe. Darum soll es im Folgenden gehen – wobei ich explizit nicht behaupte, dass das nicht anders einfacher geht! Hier nur meine Versuche und Erkenntnisse, vielleicht helfen sie ja wem.

Überlegung 1: Man geht vom erkannten Gesicht aus:

Daraus ergibt sich ein Bereich um das Gesicht

, in dem man auf Basis der Tiefeninformationen Kopf von Wand unterscheidet. Vorteil wäre, dass zB blonde Haare nicht für Wand gehalten werden. Aber #1: Man hat zwar die Tiefendaten des Kinectsensors, aber diese kann man nicht “mal eben” über die RGB-Daten der Kamera legen. Das liegt (neben den unterschiedlichen Auflösungen der beiden Sensoren) am sogenannten “Schatten”, der dadurch entsteht, dass Tiefensensor und Kamera eben zwei diskrete Sensoren sind, die nebeneinander liegen. Der Tiefensensor guckt also “von der Seite” auf die Szene der Kamera. Im folgenden Bild durch den schwarzen, nunja, Schatten visualisiert:

Dessen ist Microsoft sich bewusst, und stellt Funktionen wie MapDepthFrameToColorFrame() bereit – mit mäßigem Erfolg:

Man beachte: Der Umriss ist schon irgendwie OK (aber in x-Richtung gestreckt?), aber vor allem rechts auf dem Bild sieht man eine Lücke. Man kann diesen Schatten zwar kompensieren, das ist aber nicht unbedingt trivial, außerdem gibt es noch das Aber #2, und das sieht man hier nicht so direkt: Der Tiefensensor hat Probleme mit Haaren. Hochstehende Frisuren werden “abgeschnitten”.

Deshalb zweiter Ansatz auf Basis von Farbwerten. Das scheint durchaus erfolgsversprechender, wie ein Paper der Universität von Maryland (lokale Kopie) nahelegt, die machen genau das (Algorithmus ab 2.1; Ergebnis dann auf Seite 5). Im ersten Schritt messen sie die Durchschnittsfarbe der Haut, und zwar unter beiden Augen und über der Nase (siehe Seite 4). Im obigen Screenshot sieht man das schon, dieser bräunliche Kasten links ist meine durchschnittliche Hautfarbe:

mit

Ergänzend (und analog) bilde ich die Durchschnittsfarbe der Wand links und rechts von dem Kasten um meinen Kopf. Dann kann ich alle Pixel des Kopfkastens durchiterieren, und Wand, Haut und Rest unterschieden:

Ich spare mir an dieser Stelle den Code der Erkennung, man muss recht viel feintunen:

  • Beispielsweise wird heller Schein auf der Haut als Wand erkannt, weshalb ich beide Hälften des gesichtes separat behandel – dadurch kann ich davon ausgehen, dass wenn der Kopf (die Wand) begonnen hat, keine Wand- (Kopf-) Pixel mehr kommen.
  • Ich gehe aber erst nach zwei entsprechend erkannten Pixeln davon aus, Kopf (Wand) gefunden zu haben, um toleranter gegen Fehlmessungen zu sein…
  • …außerdem verwende ich unterschiedliche Thresholds für jede Hälfte, da das Licht an meinem Schreibtisch von der linken Seite kommt – rechts ist dadurch dunkler.
  • Ergänzend addiere ich einen Offset (“lightningOffsetLeft” im obigen Code; kann auch negativ sein), um diesen Lichteinfall zu kompensieren

Das geht dann schon ganz gut:

Und man könnte sich denken: Wow, sogar Bart und Brille werden “erkannt”. Nicht so schnell, so sieht jeder aus, auch Leute ohne Bart und Brille.

Mir geht es hier nur um Frisur und (primär) Haarfarbe, deshalb ignoriere ich die Pixel im Gesicht: Dafür lege ich um die Nasenspitze eine Ellipse, und ignoriere alle Pixel innerhalb dieser Ellipse:

Außerdem ignoriere ich bis auf Weiteres alle Pixel unterhalb der Nase, um mir Probleme mit der vergleichsweise schattigen, also dunkleren, Region unter dem Kinn zu ersparen. Das Ergebnis:

ist nicht soo schlecht! Aber (es gibt immer ein aber): Man bekommt tatsächlich Probleme, wenn Haut, Haar (und hier die Wand) ähnliche Farben haben (auch hier ignoriere ich alle Pixel unterhalb der Nase!):

(Das Gesicht habe ich nachträglich verfälscht. Danke an M. für’s Modellsitzen!)

Ideen, das zu vermeiden, wären zum Beispiel die Einbeziehung von Kantenerkennung, um “Ende” der Wand und “Beginn” der Haut zu erkennen… oder gibt es weitere Vorschläge? Bis auf Weiteres soll es das aber erstmal gewesen sein. HTH!

UPDATE

PS, hier noch ein paar Libraries, die nützlich sein könnten (ungetestet):

  • headtrackr, “Javascript library for headtracking via webcam and WebRTC/getUserMedia”
  • FaceTracker, “Real time deformable face tracking in C++ with OpenCV 2.”
  • ofxFaceTracker, “ASM face tracking addon based on Jason Saragih’s FaceTracker.”

Leave a Reply

Your email address will not be published.

Ich erkläre mich damit einverstanden, dass alle eingegebenen Daten und meine IP-Adresse zum Zweck der Spamvermeidung durch das Programm Akismet in den USA überprüft und gespeichert werden. Weitere Informationen zu Akismet und Widerrufsmöglichkeiten.