package com.transmote.flar { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.media.Camera; import flash.media.Video; /** * use the contents of a Camera feed as a source image for FLARToolkit marker detection. * * @author Eric Socolofsky * @url http://transmote.com/flar */ public class FLARCameraSource extends Sprite implements IFLARSource { public static var MAX_CAMERA_WIDTH:Number = 640; public static var MAX_CAMERA_HEIGHT:Number = 480; private var sourceWidth:Number; private var sourceHeight:Number; private var _downsampleRatio:Number; private var _isMirrored:Boolean; private var camera:Camera; private var video:Video; private var videoContainer:Sprite; private var snapshotBitmap:Bitmap; private var snapshot:BitmapData; private var sampleMatrix:Matrix; /** * constructor. * @param width camera input display and marker tracking source image width. * @param height camera input display and marker tracking source image height. * @param fps framerate of camera capture. * @param downsampleRatio amount to downsample camera input. * adjust to balance between image quality and marker tracking performance. * a value of 1.0 results in no downsampling; * a value of 0.5 (the default) downsamples the camera input by half. * downsampling is measured against FLARCameraSource.MAX_CAMERA_WIDTH / MAX_CAMERA_HEIGHT * (default 640x480); change these values prior to instantiation to support * cameras with a higher native resolution than 640x480. * @param isMirrored set to true to flip the camera source horizontally, * to make the screen appear as a mirror to the user. * defaults to true. */ public function FLARCameraSource (width:int=640, height:int=480, fps:Number=30, downsampleRatio:Number=0.5, isMirrored:Boolean=true) { this.init(width, height, fps, downsampleRatio, isMirrored); } /** * update the BitmapData source. */ public function update () :void { this.snapshot.draw(this.video, this.sampleMatrix); } /** * retrieve the BitmapData source used for analysis. * NOTE: returns the actual BitmapData object, not a clone. */ public function get source () :BitmapData { return this.snapshot; } /** * size of BitmapData source used for analysis. */ public function get sourceSize () :Rectangle { return new Rectangle(0, 0, this.sourceWidth, this.sourceHeight); } /** * amount by which the BitmapData source is downsampled. */ public function get downsampleRatio () :Number { return this._downsampleRatio; } /** * set to true to flip the camera image horizontally. */ public function get isMirrored () :Boolean { return this._isMirrored; } public function set isMirrored (val:Boolean) :void { this._isMirrored = val; } private function init (width:int, height:int, fps:Number, downsampleRatio:Number, isMirrored:Boolean) :void { this._downsampleRatio = downsampleRatio; this._isMirrored = isMirrored; // calculate source size given camera default width / height and downsampleRatio var rawSourceWidth:Number = MAX_CAMERA_WIDTH * this._downsampleRatio; var rawSourceHeight:Number = MAX_CAMERA_HEIGHT * this._downsampleRatio; // set up Camera and Video to display it var i:uint = Camera.names.length; while (i--) { // auto-select built-in USB camera (i.e. Mac ISight) if (Camera.names[i] == "USB Video Class Video") { break; } } this.camera = Camera.getCamera(i.toString()); this.camera.setMode(rawSourceWidth, rawSourceHeight, fps); this.video = new Video(rawSourceWidth, rawSourceHeight); this.video.attachCamera(this.camera); // nest Video inside another sprite, // to ensure transformations appear in BitmapData sampling this.videoContainer = new Sprite(); this.videoContainer.addChild(this.video); // scale and crop camera source to fit within specified width / height. var fitWidthRatio:Number = rawSourceWidth / width; var fitHeightRatio:Number = rawSourceHeight / height; if (fitHeightRatio < fitWidthRatio) { // fit to height, center horizontally, crop left/right edges this.sourceWidth = fitHeightRatio*width; this.sourceHeight = rawSourceHeight; this.video.x = -0.5 * (rawSourceWidth - this.sourceWidth); } else { // fit to width, center vertically, crop top/bottom edges this.sourceWidth = rawSourceWidth; this.sourceHeight = fitWidthRatio*height; this.video.y = 0.5 * (rawSourceHeight - this.sourceHeight); } // construct transformation matrix used when updating BitmapData source if (this._isMirrored) { this.sampleMatrix = new Matrix(-1, 0, 0, 1, this.sourceWidth-this.video.x, -this.video.y); } else { this.sampleMatrix = new Matrix(1, 0, 0, 1, -this.video.x, -this.video.y); } // set up BitmapData to use as FLARToolkit source, // and Bitmap to display it on-screen. this.snapshot = new BitmapData(this.sourceWidth, this.sourceHeight, false, 0); this.snapshotBitmap = new Bitmap(this.snapshot); this.snapshotBitmap.scaleX = this.snapshotBitmap.scaleY = width / this.sourceWidth; this.addChild(this.snapshotBitmap); } } }