Raspberry Video Camera – Teil 21: Konzept einer selbstlernenden Farberkennung

Damit ein Programm die Farben eines Objekts in den Videobildern erkennen kann, muss das Programm erst mal wissen, nach welchen Farben es suchen soll. Bisher hatten wir das von Hand gemacht und die typischen Farben der Eichhörnchen aus den Histogrammen von Beispielbildern abgelesen. Wie wäre es nun, wenn das Trigger-Programm das selbst erledigt und anhand einiger bereitgestellter Beispielbilder die Objektfarben erlernt? Und wie wäre es weiterhin, wenn das Programm laufend den Bildhintergrund analysiert, der sich über den Tag sonnenlichtbedingt ändert, und dessen aktuelle Farben in die Auswertung mit einbezieht? In diesem Artikel stelle ich ein paar Konzepte für eine selbstlernende Farberkennung vor, im nächsten folgt dann das entsprechende Python-Programm.

Hier noch einmal das Video des Bildhintergrunds im Tagesverlauf mit dem zugehörigen Hue-Saturation-Histogramm. Die Eichhörnchen-Videos gibt es natürlich weiterhin in meinem YouTube-Kanal.

Was bisher geschah …

Ich blicke noch einmal etwas zurück, um diejenigen Leser abzuholen, die nicht alle Artikel dieser Serie gelesen haben. Die Raspberry Video Cam ist eine fest installierte Outdoorkamera, die vorrangig Eichhörnchen auf der Dachterrasse erkennen und auf Full-HD Video aufzeichnen soll. Im Softwareteil dieser Serie geht es hauptsächlich darum, wie denn die Kamera am besten auszulösen ist, wenn ein Eichhörnchen vor die Linse kommt. Ein Bewegungssensor war der erste Ansatz und der zweite der Versuch, die typischen Eichhörnchenfarben im Videobild zu erkennen. Dabei wurden die Farben bisher von Hand aus den Histogrammen von Beispielbildern ausgelesen und fest ins Programm hinein kodiert. Der Vorgang der Festlegung der Eichhörnchenfarben soll nun automatisiert werden.

Selbstlernende Farberkennung

„Selbstlernend“ ist ein großes Wort, aber so schlau muss eine Software gar nicht sein – die folgenden Komponenten genügen:

  • Beispielbilder: also positive Beispielfotos, auf denen sich die gesuchten Objekte befinden – und nur diese. Die Bilder müssen wir selbst erzeugen und dem Programm zur Verfügung stellen.
  • Histogrammerzeugung aus den Beispielbildern: Denn im Histogramm stecken die Farbinformationen.
  • Verdichten der Histogramme auf einige wenige: Damit der Vergleichsaufwand in Grenzen bleibt.
  • Vergleich durch Histogramm-Rück-Projektion: Das ist die eigentliche Auswertung, bei der ein aktuelles Videobild auf die Histogrammfarben hin überprüft wird.
  • Optimierung durch Herausrechnen der Hintergrundfarben: Wenn die Beispielbilder mit den Eichhörnchen die Positivinformation darstellen, dann ist der Hintergrund quasi die Negativinformation, dessen Farben beim Vergleich nicht berücksichtigt werden sollen.

Beispielbilder

Im Falle der Eichhörnchen sind das zumindest mal zwei: Ein Bild vom rotbraunen und eins vom dunkelbraunen Hörnchen. Dazu können aber durchaus auch noch welche von anderen Tieren kommen, Rabenkrähen und Buntspechte zum Beispiel bei mir. Und dann sollten von jedem Tier verschiedene Ansichten und verschiedene Lichtsituationen abgebildet sein. Also gerne 10 oder mehr Bilder pro Tier. Woher die Bilder nehmen? Wer bereits die Raspberry Cam mit Bewegungssensor im Einsatz hat, der hat sicher zahlreiche Videos mit Eichhörnchen zur Verfügung. Wer nicht, der borgt sich meine aus. Wie wir Einzelbilder aus einem Video entnehmen können und dann in der Bildbearbeitung das Eichhörnchen vom Hintergrund befreien, das habe ich bereits in meinem Artikel zur Farbkalibrierung beschrieben. Ich möchte aber noch kurz auf den Bildhintergrund der Beispielbilder eingehen. Ich selbst lasse den Hintergrund transparent und The Gimp speichert das als schwarze Farbe ab. Die Hintergrundfarbe ist wichtig, denn sie muss später im Programm extra herausgerechnet werden. Aufgrund der großen Fläche ist die Hintergrundfarbe zwar die dominante Farbe des Bildes, sie interessiert uns aber nicht wirklich. Schwarz ist für mich ok, weil weder das rotbraune noch das dunkelbraune Eichhörnchen wirklich schwarz sind. Für andere Anwendungsfälle, bei denen auch schwarz detektiert werden soll, sollte aber eine andere Farbe gewählt werden. Und zwar eine, die in keinem der Zielobjekte vorkommt. Also ruhig Pink, wenn es um die Erkennung schwarzer Katzen geht. Bei der Bildgröße bietet es sich an, die gleiche Größe zu nehmen wie die Bilder, die später aus dem Videodatenstrom kommen, also 640×360 Pixel. Was den Dateinamen angeht, so empfehle ich den extrem kurz zu halten, also zum Beispiel r1.png bis r10.png für die roten und b1.png bis b10.png für die dunkelbraunen Eichhörnchen. Im nächsten Artikel, wenn es um das Python-Programm zur selbstlernenden Farberkennung geht, werden wir sehen, warum das sinnvoll ist. Alle Beispielbilder kommen dann auf dem Raspberry Pi zusammen in ein Unterverzeichnis – meins heißt samples/. Wer keine schwarze Farbe für den Hintergrund nimmt, der sollte testweise eines seiner Bilder in Python und OpenCV laden, es in den HSV-Farbraum überführen und das Bildarray mit print ausgeben. So lässt sich leicht ablesen, wie die Hintergrundfarbe kodiert ist. Wie man das macht, habe ich in meinem Artikel „Wie Computer Farben sehen“ beschrieben.

Histogrammerzeugung

Das Python-Programm wird das Verzeichnis samples/ durchsuchen, alle Bilddateien darin der Reihe nach einlesen und aus den Bildern 2D-Histogramme berechnen. Wie das geht, habe ich im vorhergehenden Artikel bereits beschrieben. Kurz zur Wiederholung: Das 2D-Histogramm kann zwei Farbkanäle (also zwei Dimensionen) gleichzeitig abbilden. Auf der X-Achse liegt Hue (Farbwert) und auf der Y-Achse Saturation (Sättigung). Die Anzahl der im Bild vorhandenen Pixel für eine Hue-Saturation-Kombination wird durch die Farbe des entsprechenden Quadrats ausgedrückt (analog zum eingeblendeten Farbbalken). Bei der Erzeugung des Histogramms ist die Anzahl der Bins eine wichtige Einstellung. Bins sind die Skalenunterteilungen  und damit die Anzahl der Unterteilungen auf einer Achse. Hier im Beispiel sind 30 Bins für Hue und ebenso 30 für die Saturation gewählt. Bei 30 Bins, aber 180 möglichen Farbwerten werden also immer 6 benachbarte Farbwerte zu einem Bin zusammengefasst und die Anzahl der vorkommenden Farbpixel zusammenaddiert. Das vereinfacht die Anzeige, vermindert das Datenvolumen, beschleunigt die Berechnung aber führt auch zu Ungenauigkeiten. Hier ist also reichlich Raum für eigene Experimente.

Verdichtung der Histogrammdaten

An sich haben wir viel Zeit um das aktuelle Kamerabild auszuwerten, nämlich genau eine Sekunde. Denn nur jede Sekunde wird ein neues Bild aus dem Videodatenstrom zur Verfügung gestellt. Mit dieser Sekunde sollten wir aber schon auskommen und das geht nicht, wenn wir endlos viele Histogramme von Beispielbildern mit dem aktuellen Kamerabild vergleichen wollen. Deshalb bietet es sich an, die Histogrammdaten dahingehend zu verdichten, dass einander ähnliche Histogramme zu einem einzigen zusammengefasst werden. Um beim Beispiel der Eichhörnchen zu bleiben, dann würden im Idealfall alle Histogramme der rotbraunen und die der dunkelbraunen Eichhörnchen zu genau zwei verbleibenden Histogrammen eingedampft. Eins für Rotbraun und eins für Dunkelbraun und in diese beiden Histogramme gehen dann alle Informationen der Einzelhistogramme ein. Wie das aussieht zeigt das folgende Schaubild. Hier werden 16 Einzelhistogramme für rotbraune Eichhörnchen zu einem einzigen Histogramm (rechts) verdichtet. Wie verdichtet man Histogramme? Das ist das schöne an OpenCV, Bilder und Histogramme sind nur Zahlen. Und zwar Zahlen, die im Falle unserer Histogramme aus zweidimensionalen NumPy-Arrays bestehen, jedes 30 x 30 Elemente groß und mit jeweils einem Wert zwischen 0 und 100. Und damit lässt sich schön rechnen. Es gibt sicher zahlreiche Möglichkeiten zwei Arrays rechnerisch zu vereinigen, ich stelle hier nur mal zwei vor: Im Beispiel oben werden die 16 Einzelhistogramme einfach addiert und das Ergebnishistogramm danach wieder auf 100 normalisiert. Dabei werden jeweils die 16 Einzelwerte an den gleichen Arraypositionen aufsummiert. Wie man sieht, erfolgt dadurch eine Art Durchschnittsbildung. Arraypositionen die oft hohe Zahlenwerte beinhalten, bleiben stark, Farb-Sättigungs-Kombinationen, die nur in wenigen Bildern mit Werten belegt sind, verschwinden eher in der breiten Masse. Das Ergebnishistogramm zeigt also eher das Signifikante, das Typische in der Fellfarbe der roten Eichhörnchen. Anders im zweiten Beispiel. Die Ausgangsdaten sind die selben, die Einzelwerte werden aber nicht addiert, sondern es wird bei jeder Verdichtung der größte Einzelwert übernommen. Nachdem so keine Einzelwerte verschwinden können, wirkt das Ergebnis voller, denn es bildet die ganze Breite des Farbspektrums ab, das in allen Einzelbildern vorkommt. Bei der Wahl der Verdichtungsmethode gibt es kein richtig oder falsch, es ist eher eine Philosophiefrage, ob man eher einen schmalbandigen Ansatz verfolgen möchte und nur auf ganz typische Farben abprüft. Mir selber ist der zweite, der breitbandige Ansatz etwas sympathischer, weil er alle Farben erst einmal mit einbezieht und nichts unter den Teppich kehrt. So bleibt mehr Farbmaterial, aus dem wir später die Hintergrundfarben herausrechnen können.

Aber wie finde ich die richtigen Histogramme, um sie mit einander zu verschmelzen? Es sollen ja nicht alle Histogramme zusammengefasst werden. Und wir wollen auch nicht von Hand hingehen und eine Sortierung vorgeben. Das ist auch gar nicht nötig, denn wir können die Ähnlichkeit von Histogrammen berechnen und dann immer die beiden ähnlichsten miteinander verschmelzen. OpenCV bietet dazu die compareHist Funktion, die zwei Histogramme vergleicht und einen Zahlenwert als Maß für die Ähnlichkeit zurück gibt. Diese Zahl liegt zwischen 0 und 1, wobei 0 für Gleichheit steht und 1 für extreme Unterschiedlichkeit. Wir brauchen nur jedes Histogramm mit allen anderen zu vergleichen und jeweils die beiden ähnlichsten Histogramme vereinigen. Das ganze wiederholen wir so oft, bis ein gewünschter Grad an Unterschiedlichkeit übrig bleibt, zum Beispiel bei einem Vergleichsergebnis von 0,5 (um die Mitte zwischen 0 und 1 zu wählen).

Histogramm-Rück-Projektion

Wir haben nun aus vielen Histogrammen einige wenige gemacht. Bei mir sind es zum Beispiel acht Histogramme, die aus ursprünglich 49 Beispielbildern übrig geblieben sind. Davon stehen drei für dunkelbraune und eins für rotbraune Eichhörnchen. Daneben habe ich noch drei Histogramme für Rabenkrähen und ein Histogramm für Buntspechte, wobei in das Histogramm für die Spechte auch drei dunkelbraune Eichhörnchen eingeflossen sind. Aber ok, wenn sie sich ähnlich sind. Die Histogramme merken wir uns, denn sie bilden die Datenbasis für die Detektoren durch die wir nun die Einzelbilder der Videokamera laufen lassen.

Im vorherigen Analyseprogramm hatten wir nur einen Detektor (genau genommen zwei, wenn wir den Hardware-Bewegungssensor mitrechnen). Die Farbanalyse funktionierte dabei so, dass wir untere und obere Grenzwerte festgelegt hatten und zwar für alle drei Farbkanäle (Hue, Saturation und Value) und alle Eichhörnchen (rote und braune). Innerhalb dieser Grenzwerte wurde dann geschaut, ob sich entsprechende Farbpixel im Bild befinden und die wurden gezählt. Die Anzahl der erkannten Farbpixel war ein Maß für Eichhörnchen oder nicht Eichhörnchen.

Nun haben wir mehrere Detektoren und jeder hat ein Histogramm für bestimmte Farbvorkommen. So ein Histogramm, das aus Musterbildern entstanden ist, lässt sich auf ein aktuelles Bild rückprojizieren. Dazu bringt OpenCV die Funktion calcBackProject mit, die als Ergebnis ein Graustufenbild liefert, in dem jedes Pixel umso heller ist, je mehr es der Farbinformation des Histogramms entspricht. Darauf wenden wir einen Schwellwert an, der das Gekräusel niedriger Werte eliminiert und erhalten eine Schwarz-Weiß-Maske unserer Erkennung. (Im Beispielbild zu sehen anhand eines dunkelbraunen Eichhörnchens.) Die verbleibenden weißen Pixel müssen nur noch gezählt werden. Dieser Vorgang erfolgt der Reihe nach für alle Detektor-Histogramme und damit wäre die selbstlernende Farberkennung eigentlich  bereits fertig. Eine Verbesserung im Sinne von Anpassung und Automatisierung können wir aber noch dazu implementieren, nämlich das:

Herausrechnen der Hintergrundfarben

Wir haben Beispielbilder unserer Zielobjekte zur Verfügung, im Sinne von Positivdaten – sie stehen für die Farben, die wir erkennen wollen. In selbstlernende Systeme füttert man aber auch gerne Negativdaten ein. In diesem Fall wären das Bilder, die Farben enthalten, die wir nicht erkennen wollen. Das soll die Erkennung verbessern, weil bekannte Negativdaten bei der Erkennung gar nicht in Betracht gezogen werden müssen. Wir haben bei den Eichhörnchen auch Negativdaten, nämlich den typischen Bildhintergrund und der ändert sich farblich auch noch beständig mit der Tageszeit, dem Wetter und auch der Jahreszeit. Wie wäre es jetzt, wenn wir alle paar Minuten – wenn gerade kein Eichhörnchen im Blick ist – die Farben des Bildhintergrunds analysieren und die aus unseren Detektor-Histogrammen herausrechnen? Der Sinn dahinter liegt darin, dass wir bei einer Farbe, die sowohl im Eichhörnchen, als auch im Hintergrund vorhanden ist, sowieso nicht entscheiden können, zu wem sie gehört. Diese Farbe würde also nur einen zweifelhaften Beitrag zur Objekterkennung liefern, so dass wir eigentlich auf sie verzichten können. Wer meine Artikelserie verfolgt hat, der kennt das G’schieß (bay.: die Umstände) mit den dunkelbraunen Eichhörnchen, die farblich nahe an den Ästen der Kiefer im Hintergrund liegen. Wenn sich nun der Farbton der Äste (und des gesamten Hintergrunds) über den Tag ändert, dann könnten immer genau diese Farben von der Farbanalyse ausgenommen werden. In der Hoffnung, dass die verbleibenden Farben in den Detektor-Histogrammen um so treffsicherer zur Anwendung gelangen können. Anschaulich wird das in folgender Grafik: Links steht das verdichtete Histogramm aller rotbraunen Eichhörnchen, das wir von oben bereits kennen und in der Mitte ein aktuelles Histogramm des Bildhintergrunds an einem sonnigen Sommerabend. Die Farben links interessieren uns für die Farberkennung, denn sie stehen für rote Eichhörnchen. Die Farben des Hintergrunds (Mitte) sind jedoch uninteressant. Im rechten Bild sind nun die Hintergrundfarben aus dem Eichhörnchen-Histogramm herausgerechnet. Das ist durch einfache Subtraktion erfolgt. Glücklicherweise gibt es farblich nur wenig Überschneidung zwischen roten Eichhörnchen und den Hintergrundfarben. Das Ergebins-Histogramm rechts verliert gegenüber dem ursprünglichen nur ein paar schwache Farbelemente links unten im rot-orangen Bereich bei recht geringer Sättigung – das dürfte für ein blasses Graubraun stehen.

Wenn nun alle paar Minuten der aktuelle Hintergrund (in der Mitte) aus den Detektor-Histogrammen (im Beispiel links) herausgerechnet wird, bevor diese zur Rückprojektion herangezogen werden, dann steht zu erwarten, dass im Leerlauf – also ohne Eichhörnchen im Bild – nur sehr wenig Farbpixel als Treffer erkannt werden. Der Unterschied beim tatsächlichen Auftritt eines Eichhörnchens sollte aber signifikant ausfallen. So weit die Theorie, im nächsten Artikel wird das Ganze dann in ein Python-Programm umgesetzt.

 


Weitere Artikel in dieser Kategorie:

2 Kommentare

  1. Detlef Brauckhoff

    Verdichtungsmethode 2 habe ich erst im Teil 22 verstanden (Programm).
    Da meine Eichhörnchen noch nicht angefüttert sind, muss ich mich beim Nachvollziehen der Beispiele mit Deinen YouTube-Videos begnügen, aber das hat ja bisher auch gut geklappt.
    Wenn alle SW-Tests gelungen sind, werde ich mit einem Bekannten zusammen 2 Systeme (Boxen) nachbauen. Alle Komponenten sind bereits bestellt.
    Nochmals vielen Dank und ein großes Lob für die ausführliche Beschreibung und die anspruchsvolle Creativität bei der Lösung der Grundaufgabe.

  2. Helmut (Beitrag Autor)

    Vielen Dank für die Anerkennung und viel Erfolg beim Nachbau.

Schreiben Sie einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.