First Flex 2 App - Webcam motion tracking
This is my first exploration into flex. To build the pong game I wanted to make this was the first thing I had to get right. I have copied the ideas and methods from a couple of tutorials I found - specifically the motion tracking tutorial by Guy Watson for flash8, and another about taking snapshots in flex. I had to convert the flash8 code to as3 / flex, and muck about with a few things but the principles remained the same.
I found these tutorials very helpful, and have replicated Guy's experiment in flex, basically tracking a handful of frames of movement and showing them in a gradually degrading colour - basically a fading motion blur, showing pixels that register movment in green.
It's not perfect, and you'll see a lot of noise in it depending on your environment and lighting situation, but if you have a webcam have a look here. It's probably very simplistic to those who are experienced with actionscript but I thought it was a cool thing to do for a "hello world" app :)
Amazingly, it's very very little code to achieve this. I have one actionscript class which is responsible for capturing the webcam stream and putting it in a panel (the left hand panel in the example). The rest is in an mxml file and is pretty smal. Here's the actionscript class:
{
import mx.containers.Panel;
import flash.media.Camera;
import flash.media.Video;
import mx.core.UIComponent;
public class WebcamPanel extends Panel
{
public var video:Video;
public function WebcamPanel(){
super();
insertWebcamVideo();
}
public function insertWebcamVideo():void {
var videoHolder:UIComponent = new UIComponent();
var camera : Camera = Camera.getCamera();
video = new Video(camera.width*2, camera.height*2);
video.attachCamera(camera);
videoHolder.addChild(video);
addChild(videoHolder);
}
}
}
I don't think this is the best way to do it, however I haven't learned enough yet about placing bitmaps on the screen do do away with it - it's an easy way to drop the snapshots etc into the display so I haven't tried to replace it yet. If anyone can add comment on a better way to draw video or bitmaps on the screen please comment!
Here's the mxml file:
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:comp="components.*"
layout="horizontal"
creationComplete="startSnapshots()">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import flash.display.BitmapData;
public var before : BitmapData = new BitmapData(320,250);
public var out : BitmapData = new BitmapData(320,250);
public var snapStack : Array = new Array;
public var maxFrames : int = 7;
public function startSnapshots() : void {
setInterval(snapshot, 10);
}
public function snapshot() : void {
var shotFrame : UIComponent = new UIComponent();
var now : BitmapData = new BitmapData(vidPanel.width,vidPanel.height);
var shotBitmap : Bitmap = new Bitmap(out);
// clear the child so we don't keep adding more and more (there must be a better way of drawing the bitmap on the screen?) if (blendPanel.numChildren > 0) {blendPanel.removeChildAt(0); }
// grab the current view now.draw(vidPanel.video);
// get a copy var done : BitmapData = now.clone();
// draw over the top of the previous snap, using the difference filter to highlight the changes done.draw(before, null, null, "difference");
// convert the negative image to show green on white (shows green pixels to trck movement) done.threshold(done,done.rect,done.rect.topLeft,">",0xFF111111,0xFF00FF00,0x00FFFFFF,false);
// push the snap onto the array snapStack.push(done.clone());
// if we have too many already then drop the oldest one if(snapStack.length > maxFrames)
{
snapStack.shift().dispose()
}
var stackHeight : int = snapStack.length;
// set value for colour deterioration (for funky fading effect) var det : Number = 255/stackHeight;
// set before to the current state, so it's ready for the next call of the function before = now.clone();
// clear the display out.fillRect(out.rect,0xFF000000);
// loop through the stack and display a degrading transition between the snaps. for(var i:int = 0; i < stackHeight; ++i)
{
//determine the current degradation var g : int = det * i
//copy all the pixels that are green in the current item in the fstack of snapshots and convert them to the degraded green //add those pixels on top of the bitmap that will displayed to the user out.threshold(snapStack[i],out.rect,out.rect.topLeft,"==",0xFF00FF00,(255<<24 | 0<<16 | g<<8 | 0),0x00FFFFFF,false);
shotFrame.addChild(shotBitmap);
blendPanel.addChild(shotFrame);
}
}
]]>
</mx:Script>
<comp:WebcamPanel id="vidPanel" width="340" height="280" />
<mx:Panel id="blendPanel" width="340" height="280" />
</mx:Application>
Again I'd be very happy to receive any comments on the code and on better ways to do things - I've basically looked at example code from flash8 and tried to just learn the relevant methods and convert it to flex.
The next step was to add detection of where exactly the motion is going - up or down, essentially - and be able to make a sprite move accordingly. I'll post the results of that later on.
http://www.tobytremayne.com/trackback.cfm?1AED47FE-A44F-C849-B24FF2FC8E627AF3

