Flash AS3 – ColorBlobDetector / Color-Tracking

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 ;).

  • Flash Color Tracking – 2005

    Grant Skinner veröffentlichte 2005 in einer seiner Präsentationen einige Ansätze zum „Color Tracking“ in AS2. Diese wurden vermutlich mit Hilfe der damals neuen BitmapData-API des Flash Player 8 erstellt.

  • Flash Color Tracking – 2008

    Im Jahr 2008 publizierte Benjamin Bojko einen „Mean Shift Tracker“ welcher im CIEL*a*b* Farbraum und unter Verwendung des Mean-Shift-Verfahrens arbeitet.

    Benjamin Bojko´s Mean Shift Tracker

  • Flash Color Tracking – 2009

    Im November 2009 veröffentlichte Jonathan Cipriano ein „Color Tracking“ Verfahren welches auf einer Transformation in den HSV-Farbraum und einem Schwellwertverfahren aufbaut. Zur Detektion der einzelnen Flächen wurde nach dem Schwellwertverfahren die getColorBoundsRect() Methode der BitmapData Klasse der Flash API genutzt. Insbesondere wenn mehrere nicht zusammenhängende Regionen die selbe Farbe aufweisen hat dieses Verfahren jedoch noch schwächen. Wie im Bild ersichtlich werden gleichfarbige Gegenstände als eine Region erkannt.

    Jonathan Cipriano AS3 HSV Color Tracker

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

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

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

links: bereinigtes S/W-Bild, rechts: grün dargestellt die Regionen

So und damit is der “ColorBlobDetector” fertig.

rot markiert die gefundenen Regionen

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));
		}

	}

}

Leave a Reply