Da ich einen “Color-Tracker” brauchte hab ich gedacht, wenn dann könnt ich ja auch eine Demo-Applikation machen und diese online stellen. Außerdem möchte ich hier den Werdegang meines “ColorBlobDetector” darstellen.
Color-Tracking / Blob-Detection, was ist das?
Zuerst mal für diejenigen die sich unter dem Begriff “Color-Tracking” nichts vorstellen können eine kurze Einleitung.
Beim Color-Tracking geht es darum zusammengehörende Bereiche gleicher Farbe in Bildern aufzufinden (Farbbildsegmentierung) und diese zu verfolgen. Zum Beispiel könnte die gesuchte Region ein roter Ball in einem Bild eines Gartens sein, wobei der Ball von einem Kind herumgerollt wird. Das Auffinden allein (worum es auch in diesem Beitrag geht) wird allgemein als “Detection” (Erkennung) bezeichnet. Daher auch der Klassenname “ColorBlobDetector”.
Das Tracking, also die Verfolgung des Balls über mehrere Bilder, benötigt wieder eine eigene Verarbeitung (meist Tracker genannt). Hier verschwimmen die Grenzen zwischen Color-Tracking und Motion-Tracking etwas da beides darauf abzielt eine Bewegung in einer Abfolge von Bildern zu erkennen.
Color-Tracking in der (Flash) Vergangenheit
Am Anfang stand natürlich die Suche nach bereits existierenden Lösungen. Also hier mal ein Auszug meiner Funde. Über Hinweise auf weitere Beispiele in Form von Kommentaren würd ich mich übrigens sehr freuen
.
Aufbau des “Color-Trackers”
Um das Problem des Color-Tracking etwas in den Griff zu bekommen habe ich es zuerst in Teilprobleme zerlegt:
- - Farbbildsegmentierung: farbige Pixel aus dem Bild extrahieren (“color segmentation”).
- - Säuberung: unerwünschte Pixel entfernen ( z.B. Rauschen).
- - Labeling: zuammenhängende Regionen finden (“blobdetection /labeling”)
Farbbildsegmentierung
Um nur die Pixel zu extrahieren, die der gesuchten Farbe entsprechen, wurde ganz einfach ein Filter auf das Bild angewandt welcher die Farbwerte jedes einzelnen Pixel in den Vergleichsfarbraum (ich habe den HSV-Farbraum gewählt) konvertiert und dann bewertet, ob dieses der gesuchten Farbe entspricht oder nicht. Wenn ja wird das Pixel mit Weiß, anderfalls mit Schwarz gefüllt.

links: das original Bild, rechts: das resultat des HSV-Filters
Säuberung
Nach der Extraktion liegen die Pixel nun als Schwarz-Weiß-Bild vor aus welchem nun “nurmehr” die einzelnen Region herausgefiltert werden müssen. Um das schnell zu erledigen empfiehlt es sich zuerst noch die Pixel, welche ohnehin uninteressant sind, auszusortieren und Löcher in großen Regionen zu schließen. Das Schließen der Löcher passiert über einen sogenannten “Closing“-Filter. Das Entfernen von zu kleinen Regionen wird dadurch erledigt, dass das gesamte Bild geblurrt wird und dann nur die Pixel ausgewählt werden welche noch völlig weiß sind.

links: Das bild nach dem HSV-Filter, rechts: das bereinigte Bild
Labeling
Für die “schnelle” Erkennung von zusammenhängenden Regionen gibts es zum Glück schon ganz passable Lösungen die auch hier zum Einsatz kommen. Grundsätzlich basieren sie auf der floodFill() und der getColorBoundsRect() Methode der BitmapData Klasse.

links: bereinigtes S/W-Bild, rechts: grün dargestellt die Regionen
So und damit is der “ColorBlobDetector” fertig.

rot markiert die gefundenen Regionen
Mini Demo
Hier nun ein kurzes Code-Beispiel wie der ColorBlobDetector zu verwenden ist:
package
{
import at.geoathome.cv.blob.ColorBlobDetector;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
/**
* @author Georg Kamptner
*
* License: Do as you wish, but don`t be evil
*/
public class Main extends Sprite
{
// image
[Embed(source="image.png", mimeType="image/png")]
protected var Img:Class;
protected var _img:Bitmap;
protected var _imgContainer:Sprite;
protected var _graphicsContainer:Sprite;
// color blob detector
protected var _colorBlobDetector:ColorBlobDetector;
private var _roi:Rectangle;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
protected function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// create image
_img = new Img() as Bitmap;
// Since Bitmap is not interactive we need a Sprite
// to handle mouse events.
_imgContainer = new Sprite();
_imgContainer.addChild(_img);
addChild(_imgContainer);
// draw the found regions into this sprite
_graphicsContainer = new Sprite();
addChild(_graphicsContainer);
// add mouse events
_imgContainer.addEventListener(MouseEvent.CLICK, detectColorUnterMouse);
// detector (this is it) - debug version needs a sprite to draw into
_colorBlobDetector = new ColorBlobDetector( 2, 2000, 2, 2000);
_colorBlobDetector.setConfig(2.0, 6.0, 9.0);
_colorBlobDetector.setThresholds(8.0, 0.20, 0.0, 0.0);
_roi = new Rectangle(0, 0, _img.width, _img.height);
}
protected function detectColorUnterMouse(e:MouseEvent):void
{
// get color unter mousecursor
var rgb:uint = _img.bitmapData.getPixel(_img.mouseX, _img.mouseY);
var r:uint = rgb >> 16;
var g:uint = rgb >> 8 & 0xff;
var b:uint = rgb & 0xff;
// detect blobs
_colorBlobDetector.detectRGB(_img.bitmapData, r, g, b, _roi);
// draw regions
_graphicsContainer.graphics.clear();
for (var i:uint = 0; i < _colorBlobDetector.numOfBlobs; ++i)
{
_graphicsContainer.graphics.lineStyle(2, 0xff0000);
_graphicsContainer.graphics.drawRect(_colorBlobDetector.blobs[i].x,
_colorBlobDetector.blobs[i].y,
_colorBlobDetector.blobs[i].width,
_colorBlobDetector.blobs[i].height);
}
// debug trace
trace("Blobs found: " + _colorBlobDetector.numOfBlobs + ", Duration in MS: " +_colorBlobDetector.detectionDuration + " in FPS: " + int(1000 / _colorBlobDetector.detectionDuration));
}
}
}