﻿package {

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.ByteArray;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.ColorTransform;
	import flash.filters.ColorMatrixFilter;
	
	public class FaceTracking extends MovieClip {
		
		private var video1	: Video;		
		private var video2	: Video;		
		private var webcam	: Camera;	


		private var sprite1			: Sprite;
		private var sprite2			: Sprite;
		private var sprite3			: Sprite;
		private var sprite4			: Sprite;
		private var sprite5			: Sprite;
		private var sprite6			: Sprite;

		//   A BitmapData is Flash's version of a JPG image in memory.
		private var bitmap1			: BitmapData;
		private var bitmap2			: BitmapData;
		private var bitmap3			: BitmapData;
		private var bitmap4			: BitmapData;
		
		//	Fun, Editable Properties
		private var VIDEO_WIDTH 			: Number = 320;				//Set 100 to 1000 to set width of screen
		private var VIDEO_HEIGHT 			: Number = 240;				//Set 100 to 1000 to set height of screen
		private var WEB_CAMERA_WIDTH 		: Number = VIDEO_WIDTH/2;	//Smaller than video runs faster
		private var WEB_CAMERA_HEIGHT 		: Number = VIDEO_HEIGHT/2;	//Smaller than video runs faster
		private var VIDEO_FRAME_RATE 		: Number = 30;				//Set 5 to 30.  Higher values = smoother video


		private var DEFAULT_POINT			 : Point = new Point(0,0);
		private var debugCount : Number = 0;
		
		
		public function FaceTracking() {

			prepareWebCam();
			
			addEventListener(Event.ENTER_FRAME, drawFrame);
		}
		
		
		private function prepareWebCam() : void {
			webcam = Camera.getCamera();
			webcam.setMode(WEB_CAMERA_WIDTH, WEB_CAMERA_HEIGHT, VIDEO_FRAME_RATE);
			
			// The original video feed
			video1 = new Video(VIDEO_WIDTH, VIDEO_HEIGHT);
			video1.attachCamera(webcam);
			sprite1 = new Sprite();
			sprite1.addChild(video1);

			// Red
			sprite3 = new Sprite();
			sprite3.x = VIDEO_WIDTH*0;
			sprite3.y = VIDEO_HEIGHT*2;

			// Green channel 
			sprite2 = new Sprite();
			sprite2.x = VIDEO_WIDTH*1;
			sprite2.y = VIDEO_HEIGHT*2;

			// Blue
			sprite5 = new Sprite();
			sprite5.x = VIDEO_WIDTH*2;
			sprite5.y = VIDEO_HEIGHT*2;

			// Result
			sprite4 = new Sprite();
			sprite4.x = VIDEO_WIDTH*2;
			sprite4.y = VIDEO_HEIGHT*0;

			// Grey
			sprite6 = new Sprite();
			sprite6.x = VIDEO_WIDTH*1;
			sprite6.y = VIDEO_HEIGHT*0;


			addChild(sprite1);
			addChild(sprite2);
			addChild(sprite3);
			addChild(sprite4);
			addChild(sprite5);
			addChild(sprite6);
			
			//   A BitmapData is Flash's version of a JPG image in memory.
            bitmap1 = new BitmapData(VIDEO_WIDTH, VIDEO_HEIGHT);
            bitmap2 = new BitmapData(VIDEO_WIDTH, VIDEO_HEIGHT);
            bitmap3 = new BitmapData(VIDEO_WIDTH, VIDEO_HEIGHT);
            bitmap4 = new BitmapData(VIDEO_WIDTH, VIDEO_HEIGHT);
		}
		
		private function drawFrame(aEvent : Event) : void {
			var startTime:Date = new Date();
			var rect:Rectangle = new Rectangle(0, 0, VIDEO_WIDTH, VIDEO_HEIGHT);

			//	Copy the latest still-frame of the webcam video
			//	into the BitmapData object for detection
			bitmap1.draw(video1);
			bitmap2 = bitmap1.clone();
            bitmap4 = bitmap1.clone();


			// Create a grayscale filter with brightness
			/*
			var r:Number = 0.33;
			var g:Number = 0.33;
			var b:Number = 0.33;
			var offset:Number = 0;

			var matrix:Array = new Array();
			matrix = matrix.concat([r, g, b, 0, offset],
								   [r, g, b, 0, offset],
								   [r, g, b, 0, offset],
								   [0, 0, 0, 1, offset]);
			var filter:ColorMatrixFilter = new ColorMatrixFilter(matrix);

			// Create an identity filter with offset = brightness
			offset = 0;
			var identity:Array = new Array();
			identity = identity.concat([r, g, b, 0, offset],
									   [r, g, b, 0, offset],
									   [r, g, b, 0, offset],
									   [0, 0, 0, 1, offset]);
			var filter2:ColorMatrixFilter = new ColorMatrixFilter(identity);
			*/


			//------------- Grey
			bitmap1.threshold(bitmap1, rect, new Point(0,0), ">", 0x00050005, 0xFFFF0000, 0x00FF00FF);
			//bitmap1.threshold(bitmap1, rect, new Point(0,0), "<", 0x00002200, 0xFFFFFFFF, 0x0000FF00);
			var greyBitmap:BitmapData = bitmap1.clone();

			//greyBitmap.applyFilter(greyBitmap, rect, new Point(0,0), filter2);
			// Replace the bitmap in the sprite with a new one
			if (sprite6.numChildren > 0) sprite6.removeChildAt(0);
			sprite6.addChildAt(new Bitmap(greyBitmap), 0);

			/*
			//------------- Red channel
			bitmap2.colorTransform(rect, new ColorTransform(1, 0, 0, 1, 0, 0));
			//bitmap2.applyFilter(bitmap2, rect, new Point(0, 0), filter);
			// Replace the bitmap in the sprite with a new one
			if (sprite3.numChildren > 0) sprite3.removeChildAt(0);
			sprite3.addChildAt(new Bitmap(bitmap2), 0);
			
			//------------- Green channel
			bitmap1.colorTransform(rect, new ColorTransform(0, 1, 0, 1, 0, 0));
			//bitmap1.applyFilter(bitmap1, rect, new Point(0, 0), filter);
			// Replace the bitmap in the sprite with a new one
			if (sprite2.numChildren > 0) sprite2.removeChildAt(0);
			sprite2.addChildAt(new Bitmap(bitmap1), 0);
			
			//------------- Blue channel
			bitmap4.colorTransform(rect, new ColorTransform(0, 0, 1, 1));
			//bitmap4.applyFilter(bitmap4, rect, new Point(0, 0), filter);
			// Replace the bitmap in the sprite with a new one
			if (sprite5.numChildren > 0) sprite5.removeChildAt(0);
			sprite5.addChildAt(new Bitmap(bitmap4), 0);
			*/
			
			//------------- Result channel
			bitmap3 = bitmap1.clone();
			bitmap3.threshold(bitmap3, rect, new Point(0,0), "<", 0x00002200, 0x00FFFFFF, 0x0000FF00);
			// Replace the bitmap in the sprite with a new one
			if (sprite4.numChildren > 0) sprite4.removeChildAt(0);
			sprite4.addChildAt(new Bitmap(bitmap3), 0);
			

			// Count markers and their positions
			debugCount = 0;
			var markerPixelCount:Number = 0;
			for (var y:uint=0; y<VIDEO_HEIGHT; y+=2){
				for (var x:uint=0; x<VIDEO_WIDTH; x+=2){
					if (eatMarkerPixels(x, y)) {
						markerPixelCount++;
					}
				}
			}
			var endTime:Date = new Date();
			markerPixelCount_txt.text = 'Markers: ' +markerPixelCount;
			debug_txt.text = 'eatMarkersPixels: ' +debugCount;
			frameTime_txt.text = 'frame: ' +(endTime.getTime() - startTime.getTime())+ ' ms';
		}
		
		// This will recursively remove all neighbouring pixels
		private function eatMarkerPixels(x, y) : Boolean {
			if (bitmap3.hitTest(DEFAULT_POINT, 0xFF, new Point(x,y))) {
				debugCount++;
				bitmap3.setPixel32(x, y, 0xDD000000);
				if (x+1 < VIDEO_WIDTH) {
					eatMarkerPixels(x+1, y);
				}
				if (y+1 < VIDEO_HEIGHT) {
					eatMarkerPixels(x, y+1);
				}
				if (x >= 0) {
					eatMarkerPixels(x-1, y);
				}
				if (y >= 0) {
					eatMarkerPixels(x, y-1);
				}
				/*
				// Try to search for more to breach the gaps
				if (x+1 < VIDEO_WIDTH) {
					if (!eatMarkerPixels(x+1, y) && x+2 < VIDEO_WIDTH) eatMarkerPixels(x+2, y);
				}
				if (y+1 < VIDEO_HEIGHT) {
					if (!eatMarkerPixels(x, y+1) && y+2 < VIDEO_HEIGHT) eatMarkerPixels(x, y+2);
				}
				if (x >= 0) {
					if (!eatMarkerPixels(x-1, y) && x-2 >= 0) eatMarkerPixels(x-2, y);
				}
				if (y >= 0) {
					if (!eatMarkerPixels(x, y-1) && y-2 >= 0) eatMarkerPixels(x, y-2);
				}
				*/
				return true;
			}
			return false;		
		}
	}
}
