Im Verlauf des letzten Jahres habe ich mich mit einigen Themen nach 20, 30 Jahren wieder beschäftigt. Eines mit dem ich mich nie beschäftigt habe, ist Mustererkennung. Und ich hätte es vermutlich auch nie getan, wenn es das F1-Photo-Archiv nicht erfordert hätte. Na ja, erfordert ist übertrieben, es war eher das ich mir Arbeit (und vor allem Zeit) ersparen wollte.
Es geht immer noch um das "Taggen", sprich den Dateinamen. Nach dem Downloaden ist bei jedem Photo das Jahr, das Rennen und der Fahrername im Dateinamen vorhanden. Dann vervollständigt die Software aus der F1-Datenbank heraus den Namen mit der Bezeichnung des Teams, des Wagens und dem Motor. Wenn auf einem Photo kein Auto, sondern nur Personen zu sehen sind, braucht das Archiv im Namen der Datei nicht alle Attribute. Im Gegenteil, die überflüssigen Attribute (Wagen, Motor) bringen bei der Suche nur unerwünschte Resultate mit sich.
Das manuelle Korrigieren wäre Lebenszeitverschwendung. Eine Muster- genauer Gesichtserkennung als JAVA Library — das muss es doch geben dachte ich. Na ja, im Prinzip war das richtig. Allerdings war es diesmal deutlich aufwändiger das ganze zum Laufen zu bringen. Und ich bin dabei doch wieder an einem "alten" Thema vorbeigekommen...




OpenCV
Wikipedia sagt:
OpenCV (englische Abk. für Open Source Computer Vision Library) ist eine freie Programmbibliothek mit Algorithmen für die Bildverarbeitung und Computer Vision. Sie ist für die Programmiersprachen C, C++, Python und Java geschrieben und steht als freie Software unter den Bedingungen der Apache 2 License. Die Entwicklung der Bibliothek wurde von Intel initiiert und bis 2013 von Willow Garage gepflegt. Nach deren Auflösung wurde sie von Itseez fortgeführt, das mittlerweile von Intel übernommen wurde.
OpenCV besteht aus Modulen für verschiedene Anwendungsfelder, u.a:
- 2D- und 3D-Merkmale
- Photogrammetrie
- Automatische Gesichtserkennung
- Gestenerkennung
- Klassifizierung, Segmentierung und Erkennung
- Tiefenbilder
- Computer Vision
- Optisches Tracking & Motion Compensation
Ferner beinhaltet OpenCV eine Bibliothek für Maschinelles Lernen.
OpenCV ist in C++ geschrieben, und damit seine primäre Schnittstelle. Es gibt noch eine ältere C-Schnittstelle, aber alle neueren Entwicklungen und Algorithmen erscheinen nur noch in der C++-Schnittstelle. Es gibt "Language-Bindings" für Python und Java und damit Anwendungsprogrammierschnittstellen (API) für diese Sprachen.
Installation
Im letzten Absatz steckt das "Thema" mit OpenCV. Man muss sich ein OpenCV System installieren, d.h. die C++ Sourcen auf der eigenen Maschine compilieren und erst dann kann man mit einer JAVA API drauf zugreifen. Das kenne ich aus der Zeit am RUS. Jede Software war als C-Source verfügbar und musste mit diversen Skripts auf der Zielplattform compiliert werden. Das war nicht immer trivial, und jede UNIX Plattform war doch irgendwie eigen. Auf dem NeXT lief alles sofort, unter dem IBM UNIX Derivat (AIX) lief es selten sofort. Seit damals sag ich "Wenn Du wissen willst wie man es nicht macht, schau wie IBM es macht". Auch um diese Plattformabhängigkeiten zu überwinden, wurde JAVA und sein Runtime JRE entwickelt.
Um es kurz zu machen, das Compilieren lief äußerst zäh, die Standard Distribution ließ sich nicht ohne weiteres compilieren.
Ich musste Homebrew installieren, aber die Installation scheiterte andauernd. Ich musste mich mit Homebrew beschäftigen und letztendlich dann ein anderes
opencv.rb File (Konfigurationsdatei für Homebrew) aus GitHub runterladen, an eine ganz bestimmte Stelle in der lokalen Homebrew Installation
kopieren und dann endlich, nach Tagen, unter Ignorieren von diversen Fehlermeldungen ließ sich OpenCV für meinen Mac compilieren. Um ehrlich zu sein, ich weiß nicht ob ich das Ding auf Anhieb nochmal installieren könnte.

Gesichtserkennung
OpenCV bringt ein vortrainiertes System mit, das eine einfache Gesichtserkennung leistet. Die API für die Gesichtserkennung ist relativ leicht aufzurufen. Wichtig ist, das:
- mit
static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }die C++ Library erst verfügbar wird - mit
faceDetector.load("/Users/eathanassiou/haarcascade_frontalface_alt.xml");die vortrainierte Gesichtserkennung geladen wird.
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.objdetect.CascadeClassifier;
public class Main {
static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); };
...
if (faceDetected(File))
{ ...
}
...
public static boolean faceDetected(String File)
{
CascadeClassifier faceDetector = new CascadeClassifier();
faceDetector.load("/Users/eathanassiou/haarcascade_frontalface_alt.xml");
Mat image = Imgcodecs.imread(File); // Reading the input image
MatOfRect faceDetections = new MatOfRect(); // Detecting faces
faceDetector.detectMultiScale(image, faceDetections);
if (faceDetections.toArray().length != 0)
return true;
return false;
}
}In MatOfRect faceDetections stehen die Koordinaten von Rechtecken, welche das Gesicht markieren.
Beim genauen Hinsehen erkennt man, dass das System Gesichter von vorne erkennt, aber keine Seitenansichten. Das kann man mit einem
anderen xml File bewerkstelligen, mit haarcascade_profileface.xml. Es gibt leider kein vortrainiertes set, das beides
kann.
...
CascadeClassifier faceDetectorLeft = new CascadeClassifier();
faceDetectorLeft.load("/Users/eathanassiou/haarcascade_profileface.xml");
Mat imageLeft = Imgcodecs.imread(File); // Reading the input image
MatOfRect faceDetectionsLeft = new MatOfRect(); // Detecting faces
faceDetectorLeft.detectMultiScale(imageLeft, faceDetectionsLeft);
if (faceDetectionsLeft.toArray().length != 0)
return true;
...Bald merkt man dann, dass man mit haarcascade_profileface.xml nur linksseitige Profile erkennt. Ist in voller
Absicht so, es ist nur mit linksseitigen Profilen trainiert worden.
Trainieren kostet Zeit, Rechnerkapazitäten und Geld. Die Entwickler von OpenCV haben gemerkt, das man das Bild nur einmal "flippen"
sprich spiegelbildlich drehen muss, und dann wird die Erkennung mit dem haarcascade_profileface.xml die rechtsseitigen
Profile erkennen. Dazu gibt es in opencv.core eine Methode flip:
...
CascadeClassifier faceDetectorRight = new CascadeClassifier();
faceDetectorRight.load("/Users/eathanassiou/haarcascade_profileface.xml");
Mat src = Imgcodecs.imread(File); //Reading the Image from the file and storing it in to a Matrix object
Mat flipped = new Mat(); //Creating an empty matrix to store the result
Core.flip(src, flipped, 1); // Flip the image
MatOfRect faceDetectionsRight = new MatOfRect(); // Detecting faces
faceDetectorRight.detectMultiScale(flipped, faceDetectionsRight);
if (faceDetectionsRight.toArray().length != 0)
return true;
...



Jetzt bleiben noch Gesichter mit Sonnenbrille oder Helm, wo man vom Gesicht nur die Augen sieht. Da gibt es weitere, passende
haarcascade*.xml Dateien, aber das hat jetzt nicht die Top Priorität ...