You are on page 1of 8

main.fla — Printed on 19.07.

2008, 08:51:20 — Page 1


/**
* @author Kai Kajus Noack
* http://media-it.blogspot.com
* @version 0.1 (July 2008)
*
* Copyright (c) 2008 Kai Kajus Noack. All rights reserved.
*
* Licensed under the CREATIVE COMMONS Attribution-NonCommercial-ShareAlike 2.0 you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://creativecommons.org/licenses/by-nc-sa/2.0/de/deed.en
*
*/

/**
* Developer's Note:
* 1. Please note, that it would actually be better (i.e. easier) to use a coloured markers
* (such as complete RED or BLUE), then it would be much easier to detect the marker in the image!
* Then you could filter using a threshold = 0xCC0000 for a red marker, or similar.
* 2. Note for the current application: Try not having a dark background! Use a bright background instead,
* which makes it easier to detect the marker (reduces flickering).
* 3. There are many parts of the code that can be optimized. Please tell me your improvements!
* 4. I used 'lockers' (Booleans) to prevent the access to methods, when it is doing the image processing.
* Maybe this is not necessary.
*/

// IMPORTS
import de.firstai.cameraClass;
import de.firstai.imageClassOpt;

import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import fl.controls.Slider;
import fl.events.SliderEvent;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.objects.Cube;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.materials.MaterialsList;
import org.papervision3d.materials.ColorMaterial;

// init checkboxes
cboxBinarize.label = "binarize";
cboxBinarize.selected = false;
cboxMarkRegions.label = "mark regions";
cboxMarkRegions.selected = false;
cboxShowExtract.label = "show extract";
cboxShowExtract.selected = false;
cboxEdgeDetect.label = "edge detection";
cboxEdgeDetect.selected = false;
cboxTrack.label = "animation1";
cboxTrack.selected = false;
cboxTrack2.label = "animation2";
cboxTrack2.selected = false;
cboxTrack3.label = "animation3";
cboxTrack3.selected = false;
// shows up if no webcam is found
alertWC.visible = false;
main.fla — Printed on 19.07.2008, 08:51:20 — Page 2

/* PREPARE MARKERS */
// create loaders, each one for a marker (to display on screen according to analysis)
var imgMarker0:Loader = new Loader();
var imgMarker1:Loader = new Loader();
var imgMarker2:Loader = new Loader();
var imgMarker3:Loader = new Loader();
imgMarker0.load(new URLRequest("assets/marker/00.png"));
imgMarker1.load(new URLRequest("assets/marker/01.png"));
imgMarker2.load(new URLRequest("assets/marker/02.png"));
imgMarker3.load(new URLRequest("assets/marker/03.png"));
imgMarker0.x = imgMarker1.x = imgMarker2.x = imgMarker3.x = 320 + 10 + 10;
imgMarker0.y = imgMarker1.y = imgMarker2.y = imgMarker3.y = 30;
imgMarker0.visible = imgMarker1.visible = imgMarker2.visible = imgMarker3.visible = false;
addChild(imgMarker0);
addChild(imgMarker1);
addChild(imgMarker2);
addChild(imgMarker3);
/* Marker END */

/* Setup CAMERA and VIDEO */

var cam:cameraClass = new cameraClass();


var video:Video = cam.camVideo;
if(video == null) {
// no webcam found
alertWC.visible = true;
}
// camera has been found
else {
video.addEventListener( Event.ACTIVATE, init );
video.x = 10;
video.y = 30;
addChild(video);
// initiate the image-processing-class, pass video-data
var imgProc:imageClassOpt = new imageClassOpt(video.width, video.height);

/** Papervision 3D START **/


// responsible for 'animation3' - the turning cube
[SWF(backgroundColor="#000000", frameRate="60")]

var myMaterials:Object;
var container:Sprite;
var scene:Scene3D;
var camera:Camera3D;
var rootNode:DisplayObject3D;

[Embed(source="../assets/cubetexture.png")] var CubeTexture:Class;


[Embed(source="../assets/psychedelictexture.png")] var PsychedelicTexture:Class;
[Embed(source="../assets/cylindertexture.png")] var CylinderTexture:Class;

// container, scene, camera and root node

// create the container, add it to the stage, position it


container = new Sprite();
addChild( container );
container.x = 650;
container.y = 170;

// create a new scene and use the container


scene = new Scene3D( container );

// create a new camera and position it


main.fla — Printed on 19.07.2008, 08:51:20 — Page 3
camera = new Camera3D();
camera.x = 3000;
camera.z = -300;
camera.zoom = 20;
camera.focus = 100;

// add a root node to the scene


rootNode = scene.addChild( new DisplayObject3D("rootNode") );
// var cubeTexture:Bitmap = new CubeTexture() as Bitmap;
var cubeMaterials:MaterialsList = new MaterialsList();
// cubeMaterials.addMaterial( new BitmapMaterial( cubeTexture.bitmapData ), "all" );
cubeMaterials.addMaterial( new ColorMaterial( 0x990000 ), "all" ); // "all" indicates that this material should be
used on all sides of the cube
cubeMaterials.addMaterial( new ColorMaterial( 0xCC1111 ), "front" );
cubeMaterials.addMaterial( new ColorMaterial( 0x330000 ), "back" );
cubeMaterials.addMaterial( new ColorMaterial( 0xdd1100 ), "left" );
cubeMaterials.addMaterial( new ColorMaterial( 0xdd1100 ), "right" );
// front, back, right, left, top, bottom & all

rootNode.addChild( new Cube( cubeMaterials, 200, 200, 200, 1, 1, 1 ), "myCube01" );

/** Papervision 3D END **/

function init(e:Event):void {
trace("#INIT\nVideo started:",e,"\n");
video.removeEventListener(Event.ACTIVATE, init);
cam.camera.addEventListener(ActivityEvent.ACTIVITY,activated);

// mirror the video


var ma:Matrix = video.transform.matrix;
ma.a = -1; // scale(-1, 0); // flip horizontal assuming that scaleX is currently 1
// apply the mirror matrix to the display object
ma.tx=video.width+video.x;
video.transform.matrix=ma;
}
function activated(e:Event):void {
if (e.type == "activity") {
cam.camera.removeEventListener(ActivityEvent.ACTIVITY,activated);
startTimer();
}
}
// time-event is called periodically and new bitmapData is passed
function startTimer():void {
var myTimer:Timer = new Timer(500);
myTimer.addEventListener("timer", pullCamImage);
myTimer.start();
}

// objects to display on stage


var bmpdBlackWhite:BitmapData = new BitmapData(video.width, video.height, false);
var bmpBlackWhite:Bitmap = new Bitmap(bmpdBlackWhite);
bmpBlackWhite.x = 10;
bmpBlackWhite.y = cboxBinarize.y + 30;

// bitmaps to hold and show the different bitmapdata


var bmpdRegions:BitmapData = new BitmapData(video.width, video.height, false);
var bmpRegions:Bitmap = new Bitmap(bmpdRegions);
var bmpdMarkerExtract:BitmapData = new BitmapData(video.width, video.height, false);
var bmpMarkerExtract:Bitmap = new Bitmap(bmpdMarkerExtract);
bmpRegions.x = bmpBlackWhite.x + bmpBlackWhite.width + 10;
bmpRegions.y = bmpBlackWhite.y;
bmpMarkerExtract.x = bmpRegions.x + bmpRegions.width + 10;
bmpMarkerExtract.y = bmpRegions.y;
main.fla — Printed on 19.07.2008, 08:51:20 — Page 4
// edge detection image
var bmpdEdge:BitmapData = new BitmapData(video.width, video.height, false);
var bmpEdge:Bitmap = new Bitmap(bmpdEdge);
bmpEdge.x = bmpMarkerExtract.x;
bmpEdge.y = 30;

// necessary variables for bitmapdata


var labels:int = 0, markerID:int = 0;
var threshold:uint = 0x363636;
txtThresh.text = "0x"+((threshold>>16 & 0xFF).toString(16)+(threshold>>8 & 0xFF).toString(16)+(
threshold & 0xFF).toString(16));

// slider component
sliThreshold.liveDragging = true;
sliThreshold.minimum = 0x000000;
sliThreshold.maximum = 0xAAAAAA;
sliThreshold.value = threshold;
sliThreshold.tickInterval = 0x222222;
sliThreshold.addEventListener(SliderEvent.CHANGE, changeHandler);
} // end of else

// handler function for the slider


function changeHandler(event:SliderEvent):void {
threshold = event.value;
txtThresh.text = "0x"+((threshold>>16 & 0xFF).toString(16)+(threshold>>8 & 0xFF).toString(16)+(
threshold & 0xFF).toString(16));
}

// Program controls
// first run check and lock variable to prevent false access
var firstRun:Boolean = true;
var busy:Boolean = true;
var alreadyOnStageBin:Boolean, alreadyOnStageReg:Boolean, alreadyOnStageExtr:Boolean, alreadyOnStageEdge
:Boolean, alreadyOnStageTrack:Boolean = false;
var extractInWork:Boolean = false;
var rotaX:int, rotaY:int, rotaZ:int;

function pullCamImage(e:Event):void {
if (!busy) {
trace("# processing");
// lock image for processing
busy=true;

// copy video-data to bitmapdata


bmpdBlackWhite.draw(video);
// calculate best threshold on start, take 42 percent for binarizing
if (firstRun) {
threshold = ( 0xFFFFFF / 255.0 ) * ( imgProc.getBestThreshold(bmpdBlackWhite) * 0.42);
txtThresh.text = "0x"+((threshold>>16 & 0xFF).toString(16)+(threshold>>8 & 0xFF).toString
(16)+(threshold & 0xFF).toString(16));
sliThreshold.value = threshold;
firstRun = false;
}

// call extract-method of imageProcessor:


// creates bounding boxes around coloured areas, extracts the marker (object of interest)
imgProc.extractBlackAreas(bmpdBlackWhite, threshold);

// mark different regions in image with different colors and get number of labels
bmpdRegions.draw(bmpdBlackWhite);
labels = imgProc.markRegions(bmpdRegions);
// create bounding boxes around coloured areas, extract marker
main.fla — Printed on 19.07.2008, 08:51:20 — Page 5
if (!extractInWork) {
extractInWork = true;
bmpdMarkerExtract.draw(imgProc.extractMarker(bmpdRegions, labels));
extractInWork = false;
}

// analyze the marker (bitmapdata) and display the analyzed type


if (!extractInWork) {
analyzeMarkerResult(bmpdMarkerExtract);
}

/* CHECKBOXES necessary for controlling */


if ( (!cboxBinarize.selected) && (alreadyOnStageBin) ) {
// if bitmap is still on stage, remove it
removeChild(bmpBlackWhite);
alreadyOnStageBin = false;
}
else if ( (cboxBinarize.selected) && (!alreadyOnStageBin) ){
addChild(bmpBlackWhite);
alreadyOnStageBin = true;
}
// checkbox
if ( (!cboxMarkRegions.selected) && (alreadyOnStageReg) ) {
// if bitmap is still on stage, remove it
removeChild(bmpRegions);
alreadyOnStageReg = false;
}
else if ( (cboxMarkRegions.selected) && (!alreadyOnStageReg) ){
addChild(bmpRegions);
alreadyOnStageReg = true;
}
// checkbox
if ( (!cboxShowExtract.selected) && (alreadyOnStageExtr) ) {
// if bitmap is still on stage, remove it
removeChild(bmpMarkerExtract);
alreadyOnStageExtr = false;
}
else if ( (cboxShowExtract.selected) && (!alreadyOnStageExtr) ){
addChild(bmpMarkerExtract);
alreadyOnStageExtr = true;
}
// checkbox
if ( (!cboxEdgeDetect.selected) && (alreadyOnStageEdge) ) {
// if bitmap is still on stage, remove it
removeChild(bmpEdge);
alreadyOnStageEdge = false;
}
else if (alreadyOnStageEdge) {
bmpdEdge.draw(bmpdBlackWhite); // alternativ bmpRegions
imgProc.edgeDetection(bmpdEdge);
}
else if ( (cboxEdgeDetect.selected) && (!alreadyOnStageEdge) ){
bmpdEdge.draw(bmpdBlackWhite);
imgProc.edgeDetection(bmpdEdge);
addChild(bmpEdge);
alreadyOnStageEdge = true;
}
if ( (!cboxTrack.selected) && (alreadyOnStageTrack) ) {
// if arrow is still on stage, remove it
removeChild(futbol);
alreadyOnStageTrack = false;
}
else if ( (cboxTrack.selected) && (!alreadyOnStageTrack) ){
main.fla — Printed on 19.07.2008, 08:51:20 — Page 6
addChild(futbol);
alreadyOnStageTrack = true;
}
}
if (alreadyOnStageTrack) {
futbol.x = futbol.x - imgProc.moveX/3;
futbol.y = futbol.y + imgProc.moveY/3;
}
if (cboxTrack2.selected) {
video.x = video.x - imgProc.moveX/3;
video.y = video.y + imgProc.moveY/3;
}

// arrow pointing in the marker direction


rotaX=0; rotaY=0; rotaZ=0;
if ( (imgProc.moveX < 30) && (imgProc.moveX > -30) && (imgProc.moveY < 0) ) {
mcArrow.rotation = 90;
rotaZ=10;
}// pointing top
else if ( (imgProc.moveX < 30) && (imgProc.moveX > -30) && (imgProc.moveY > 0) ) {
mcArrow.rotation = -90;
rotaZ=-10;
}// pointing to bottom
else if ( (imgProc.moveY > 0) && imgProc.moveX > 0) {
mcArrow.rotation = -45;
rotaY=10;
}// bottom-left
else if ( (imgProc.moveY < 0) && imgProc.moveX > 0) {
mcArrow.rotation = 45;
rotaX=10; rotaZ=10;
}// top-left
else if ( (imgProc.moveY < 0) && imgProc.moveX < 0) {
mcArrow.rotation = 135;
rotaX=-10; rotaY=-10;
}// top-right
else if ( (imgProc.moveY > 0) && imgProc.moveX < 0) {
mcArrow.rotation = 225;
rotaY=-10;
}// bottom-right

if (cboxTrack3.selected) {
rootNode.rotationX += rotaX;
rootNode.rotationY += rotaY;
rootNode.rotationZ += rotaZ;
// render the camera to see the changes
scene.renderCamera( camera );
}

busy = false;
}

function analyzeMarkerResult(image:BitmapData):void {
var countWhite:int, countColor:int = 0;
var ratioWhite:Number = 0;

// count all white values


for (var yp:int = 0; yp < image.height; yp++) {
for (var xp:int = 0; xp < image.width; xp++) {
// if pixel is found then count it
if(image.getPixel(xp,yp)==0xFFFFFF) {
countWhite++;
}
// no pixel but no yellow background either (image consists of color and white)
main.fla — Printed on 19.07.2008, 08:51:20 — Page 7
// calculate the relation correctly
else if (image.getPixel(xp,yp)!=0xFFFF00) {
countColor++;
}
}
}
// check if a complete yellow image was thrown back (no fitting marker was found before)
if ( (countColor==0) && (countWhite == 0) ) {
imgMarker0.visible = imgMarker1.visible = imgMarker2.visible = imgMarker3.visible = false;
trace("# SORRY - NO MARKER FOUND #");
}
else {
ratioWhite = (Number) (countWhite/(countWhite+countColor));
/// trace("countWhite:" +countWhite, "countColor:" + countColor, "white-rel: " + ratioWhite + "\n");

imgMarker0.visible = imgMarker1.visible = imgMarker2.visible = imgMarker3.visible = false;

// compare values with values of the standard markers (which has been tested before)
if ( (ratioWhite > 0.50) && (ratioWhite <= 0.75) && (!imgMarker3.visible) ) {
imgMarker3.visible = true; // white box
}
else if ( (ratioWhite > 0.22) && (ratioWhite <= 0.50) && (!imgMarker1.visible)) {
imgMarker2.visible = true; // cross
}
else if ( (ratioWhite > 0.10) && (ratioWhite <= 0.22) && (!imgMarker2.visible)) {
imgMarker1.visible = true; // big point
}
else if ( (ratioWhite > 0.01) && (ratioWhite <= 0.10)&& (!imgMarker0.visible) ){
imgMarker0.visible = true; // small point
}
}
}

// dragger function for futbol


function initDragger(mc:MovieClip):void {
mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
mc.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
function mouseDownHandler(e:MouseEvent):void {
e.currentTarget.startDrag();
}
function mouseUpHandler(e:MouseEvent):void {
e.currentTarget.stopDrag();
}
initDragger(futbol);

/* trace("activity level: "+cam.camera.activityLevel);


trace("cam FPS: "+cam.camera.currentFPS);
trace("motion level: "+cam.camera.motionLevel);
trace("bandwidth: "+cam.camera.bandwidth);
// setQuality(bandwidth:int, quality:int):void // Sets the maximum amount of bandwidth per second or the required picture quality of
the current outgoing video feed.
//}
*/

// code for runtime measure


// var time:Number = getTimer();
// # execution of code
// trace("## time used:", getTimer() - time, "ms");
main.fla — Printed on 19.07.2008, 08:51:20 — Page 8
// Optimization tips from: www.visualharmonics.co.uk/actionscript-optimizations/
// Always typecast objects taken from arrays before using their methods,
// as array elements have no defined type (otherwise AS’s native dynamic typing will slow down your
// code). The as keyword may be used for this. (AS2, AS3)
// To hide an object on the stage, remove it from the Display List with removeChild().
// Unlike AS2, It will continue to exist until such time as you wish to redisplay it,
// so long as there is another object with a reference to it. (AS3)

You might also like