forked from: Simple Camera Tracking
// forked from forresto's Simple Camera Tracking
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
import flash.text.TextField;
import flash.utils.ByteArray;
public class FaceTrack extends Sprite
{
private var backwardsMatrix:Matrix;
private var v:Video;
private var vidmap:BitmapData;
private var overlay:Sprite;
private var obmp:BitmapData;
private var targetRect:Rectangle;
private var histo:Vector.<Vector.<Vector.<Number>>>;
private var cam:Camera;
private var lastPoint:Point;
private var lpRect:Rectangle;
private var going:Boolean;
private var instructs:TextField;
private static const TwoOverPI:Number = 2.0 / Math.PI;
private var canvas:Sprite;
private var bm:Bitmap;
public function FaceTrack():void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
var left:String = "left";
going = false;
cam = Camera.getCamera();
v = new Video(640, 480);
v.attachCamera(cam);
v.scaleX = -1;
v.x = v.width;
addChild(v);
stage.addEventListener("click", doCapture);
backwardsMatrix = new Matrix( -1, 0, 0, 1, v.width, 0);
vidmap = new BitmapData(640, 480);
canvas = new Sprite();
canvas.transform.perspectiveProjection = this.transform.perspectiveProjection;
bm = new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00ffffff));
bm.alpha = 0.5;
addChild(bm);
overlay = new Sprite();
targetRect = new Rectangle(0, 0, 80, 80);
addChild(overlay);
instructs = new TextField();
instructs.autoSize = left;
instructs.background = true;
instructs.backgroundColor = 0xffffff;
instructs.text = "Click your face (or something else) to begin tracking.";
instructs.x = stage.stageWidth / 2 - instructs.width / 2;
instructs.y = stage.stageHeight / 2;
addChild(instructs);
obmp = new BitmapData(80, 80, false);
}
private function doCapture(e:Event):void {
vidmap.draw(v, backwardsMatrix);
var halfdim:int = 80 / 2;
lastPoint = new Point(stage.mouseX, stage.mouseY);
lpRect = new Rectangle(stage.mouseX - halfdim, stage.mouseY - halfdim, 80, 80);
obmp.copyPixels(vidmap, lpRect, new Point());
histo = new Vector.<Vector.<Vector.<Number>>>(16, true);
for (var i:int = 0; i < 16; i++) {
histo[i] = new Vector.<Vector.<Number>>(16, true);
for (var j:int = 0; j < 16; j++) {
histo[i][j] = new Vector.<Number>(16, true);
}
}
getHisto(obmp);
if (!going) {
going = true;
removeChild(instructs);
addEventListener(Event.ENTER_FRAME, trackFrame);
}
}
private function getHisto(bd:BitmapData):void {
//histo = bd.histogram();
var i:int, j:int, k:int;
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
for (k = 0; k < 16; k++) {
histo[i][j][k] = 0;
}
}
}
var ba:Vector.<uint> = bd.getVector(bd.rect);
var r:int, g:int, b:int;
var px:uint;
var m:Number, n:Number;
var stepY:Number = 1.0 / (80);
var stepX:Number = 1.0 / (80);
for (i = 0, m = -0.5; i < bd.height; i++, m+=stepY) {
for (j = 0, n = -0.5; j < bd.width; j++, n+=stepX) {
px = ba[i * bd.width + j];
r = (px >> 20) & 0xf;
g = (px >> 12) & 0xf;
b = (px >> 4) & 0xf;
histo[r][g][b]+= epachnikov(n,m);
}
}
}
private function epachnikov(xnorm:Number, ynorm:Number):Number {
return TwoOverPI * (1 - (xnorm^2 + ynorm * ynorm));
}
private function drawOverlay(r:Rectangle):void {
overlay.graphics.clear();
overlay.graphics.lineStyle(3, 065895);
overlay.graphics.drawCircle(r.x + r.width / 10, r.y + r.height / 2, r.width / 2);
}
private function trackFrame(event:Event):void {
var i:int;
var halfdim:int = 80 / 2;
var weight:Number;
var r:int, g:int, b:int;
var xsum:Number;
var ysum:Number;
var cpix:uint;
var px:Vector.<uint>;
var newx:Number;
var newy:Number;
vidmap.lock();
obmp.lock();
vidmap.draw(v, backwardsMatrix);
var weightsum:Number;
var origin:Point = new Point();
for (var q:int = 0; q < 20; q++){ //arbitrarily max iterations.
obmp.copyPixels(vidmap, lpRect, origin);
xsum = 0;
ysum = 0;
weightsum = 0;
px = obmp.getVector(obmp.rect);
i = 0;
for (var n:int = -halfdim; i < 80; i++, n++) {
for (var j:int = 0, m:int = -halfdim; j < 80; j++, m++) {
cpix = px[i*80 +j];
r = (cpix >> 20) & 0xf;
g = (cpix >> 12) & 0xf;
b = (cpix >> 4) & 0xf;
weight = (histo[r][g][b]); // / numpx;
//weight = properHisto[r][g][b];// / numpx;
weightsum += weight;
xsum += m * weight;
ysum += n * weight;
}
}
newx = xsum / weightsum; // / sqnpx;
newy = ysum / weightsum; // sqnpx;
//trace("newx: " + newx + ", newy: " + newy);
lastPoint.x += Math.round(newx);
lastPoint.y += Math.round(newy);
if (lastPoint.x < halfdim) {
lastPoint.x = halfdim;
}else if (lastPoint.x > vidmap.width - halfdim) {
lastPoint.x = vidmap.width - halfdim;
}
if (lastPoint.y < halfdim) {
lastPoint.y = halfdim;
}else if (lastPoint.y > vidmap.height - halfdim) {
lastPoint.y = vidmap.height - halfdim;
}
lpRect.x = lastPoint.x - halfdim;
lpRect.y = lastPoint.y - halfdim;
if (newx*newx + newy*newy < 1) {
break;
}
}
vidmap.unlock();
obmp.unlock();
targetRect.x = lastPoint.x - halfdim;
targetRect.y = lastPoint.y - halfdim;
drawOverlay(targetRect);
bm.bitmapData.lock();
bm.bitmapData.fillRect(bm.bitmapData.rect, 0x00000000);
bm.bitmapData.unlock();
}
}
}