Computer Vision - OpenCV

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 übetrieben, 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 Gesichterkennung 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 vorbei gekommen...


OpenCV

Wikipedia sagt:

OpenCV (englische Abk. für Open Source Computer Vision Library[3]) 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:

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. es die C++ Sourcen auf der eigenen Maschine compilieren und erst dann kann man mit einer JAVA API drauf zu greifen. 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 den 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ßert zäh, die Standard Distribution liess 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 GitHib runterladen, an eine ganz bestimmte Stelle in der lolaklen Homebrew Installation kopieren und dann endlich, nach Tagen, unter Ignorieren von diversen Fehlermeldungen liess sich OpenCV für meinen Mac compilieren. Um ehrlich zu sein, ich weiss nicht ob ich das Ding auf Anhieb nochmal installieren könnte.


Gesichtserkennung

OpenCV bringt ein vortrainiertes System mit, das eine einfache Gesichteserkennung leistet. Die API für die Gesichtserkkennung ist relativ leicht aufzurufen. Wichtig ist, das :
 
                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 Gesicher von vorne erkennt, aber keine Seitenansichten. Das kan man mit einem anderen xml File bewerkstelligen, mit haarcascade_profileface.xml . Es gibt leider kein vortrainiertes set, dass 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 linksseitgen 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 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 ...