/****************************************************************************\
*
* (C) 2009 by Imagination Computer Services GesmbH. All rights reserved.
*
* Project: flare
*
* @author Stefan Hynst
*
* $Id: FlareTracker.as 95 2010-03-25 12:57:05Z imagination\stefan $
*
\****************************************************************************/
package at.imagination.flare
{
import cmodule.libFlareTracker.CLibInit;
import flash.display.BitmapData;
import flash.display.Stage;
import flash.utils.ByteArray;
// ----------------------------------------------------------------------------
/**
* FlareTracker is a wrapper class providing convenient methods to use the
* functions provided by libFlareTracker.
For an example on how to use it see
* samples/TestTracker
.
*/
public class FlareTracker implements IFlareTracker
{
/**
* The FlareTracker.MARKER_UNDEFINED constant defines the value of the markerType
* property returned by getTrackerResults() when a marker of an unkown type is found.
*/
public static const MARKER_UNDEFINED:int = 0;
/**
* The FlareTracker.MARKER_SIMPLE_ID constant defines the value of the markerType
* property returned by getTrackerResults() when a simple-id marker is found.
*/
public static const MARKER_SIMPLE_ID:int = 1;
/**
* The FlareTracker.MARKER_BCH constant defines the value of the markerType
* property returned by getTrackerResults() when a BCH marker is found.
*/
public static const MARKER_BCH:int = 2;
/**
* The FlareTracker.MARKER_BCH2 constant defines the value of the markerType
* property returned by getTrackerResults() when a BCH2 marker is found.
*/
public static const MARKER_BCH2:int = 3;
/**
* The FlareTracker.MARKER_FRAME constant defines the value of the markerType
* property returned by getTrackerResults() when a frame marker is found.
*/
public static const MARKER_FRAME:int = 4;
/**
* The FlareTracker.MARKER_SPLIT constant defines the value of the markerType
* property returned by getTrackerResults() when a split marker is found.
*/
public static const MARKER_SPLIT:int = 5;
/**
* The FlareTracker.MARKER_DATAMATRIX constant defines the value of the markerType
* property returned by getTrackerResults() when a data matrix marker is found.
*/
public static const MARKER_DATAMATRIX:int = 6;
// ------------------------------------------------------------------------
private var m_CLib:CLibInit;
private var m_logFunc:Function;
private var m_fileLoader:FilePreloader;
private var m_trackerLib:Object;
private var m_stage:Stage;
private var m_camFile:String;
private var m_camWidth:uint;
private var m_camHeight:uint;
private var m_initDoneCB:Function;
private var m_alcMemory:ByteArray = null;
private var m_alcImagePointer:uint;
private var m_alcTrackerResultPointer:uint;
private var m_alcTrackerResult2DPointer:uint;
// ------------------------------------------------------------------------
/**
* constructor
*/
public function FlareTracker()
{
m_logFunc = null;
m_CLib = new CLibInit();
m_trackerLib = m_CLib.init();
}
// ------------------------------------------------------------------------
/**
* Returns the version of flareTracker
*
* @return Version number as String formatted as major.minor.build
*/
public function getVersion():String
{
return (m_trackerLib.getVersion());
}
// ------------------------------------------------------------------------
/**
* Sets a logging function: Use this to display logging output from libFlareTracker.
*
* @param obj If the logging function is a method of an object, set this to
* the method's object. Otherwise pass null
*
* @param logger The logging function that will be called from libFlareTracker.
* The function must be of type function(int, String):void
*
* @param level Only produce logging output for log-levels <= level
*/
public function setLogger(obj:Object, logger:Function, level:uint):void
{
m_logFunc = logger;
m_trackerLib.setLogger(obj, logger, level);
}
// ------------------------------------------------------------------------
/**
* Initializes the tracker. This needs to be called before any other method is called
* (with the exception of getVersion()
amd setLogger()
)
*
* @param stage The application's stage.
*
* @param dataPath Path were the datafiles (camera ini-file and license file) are located.
*
* @param camFile Name of the camera initalization file.
*
* @param camWidth Width of the camera input in pixels.
*
* @param camHeight Height of the camera input in pixels.
*
* @param initDoneCB Callback function to be invoked, when initialization has
* finished. This is necessary, because all input files will be loaded
* asynchronously before libFlareNFT can initialize the tracker.
* The function must be of type function():void
*/
public function init(stage:Stage,
dataPath:String, camFile:String, camWidth:uint, camHeight:uint,
initDoneCB:Function):void
{
m_stage = stage;
m_camWidth = camWidth;
m_camHeight = camHeight;
m_camFile = camFile;
m_initDoneCB = initDoneCB;
if (m_stage != null)
{
// get absolute path and replace backslashes with slashes
var basePath:String = m_stage.loaderInfo.loaderURL.replace(/[\\]/g, "/");
// cut off query part
basePath = basePath.slice(0, basePath.indexOf("?"));
// cut off file name, keep last slash
basePath = basePath.slice(0, basePath.lastIndexOf("/") + 1);
// create preloader:
// all files needed by libFlareTracker have to be preloaded and passed
// to alchemy via m_Clib.supplyFile()
//
m_fileLoader = new FilePreloader(fileLoadedCB);
m_fileLoader.loadStart();
m_fileLoader.load("flareTracker.lic", basePath);
m_fileLoader.load(m_camFile , dataPath);
m_fileLoader.loadEnd();
}
}
// ------------------------------------------------------------------------
/**
* Adds a marker detector to the tracker. The following marker types are
* supported: simple id, BCH, BCH2, frame marker, split marker,
* data matrix marker.
* Call this method once for every marker type you want to track for.
*
* @param markerType Type of marker to track for. Pass one of the following values:
*
FlareTracker::MARKER_SIMPLE_ID
to detect simple id markers.
*
* param1
specifies the border width of the marker -
* more precisely this is the ratio borderWidth/markerWidth
.
* If this parameter is not passed, a default value of 0.125 is used.
*
* param2
specifies the width of the marker in millimeters.
* If this parameter is not passed, a default value of 80 is used.
*
FlareTracker::MARKER_BCH
to detect BCH markers.
*
* param1
specifies the border width ratio (defaults to 0.125)
*
* param2
specifies the width of the marker in millimeters (defaults to 80)
*
FlareTracker::MARKER_BCH2
to detect BCH2 markers.
*
* param1
specifies the border width ratio (defaults to 0.1875)
*
* param2
specifies the width of the marker in millimeters (defaults to 80)
*
FlareTracker::MARKER_FRAME
to detect frame markers.
*
* param1
specifies the border width ratio (defaults to 0.04545)
*
* param2
specifies the width of the marker in millimeters (defaults to 100)
*
FlareTracker::MARKER_SPLIT
to detect split markers.
*
* param1
specifies the width of the marker in millimeters (defaults to 61)
*
* param2
specifies the height of the marker in millimeters (defaults to 90)
*
FlareTracker::MARKER_DATAMATRIX
to detect data matrix markers.
*
* param1
specifies the border width ratio (defaults to 0.035)
*
* param2
is not used.
*
true
on success.
*/
public function addMarkerDetector(markerType:uint, param1:Number = 0, param2:Number = 0):Boolean
{
return m_trackerLib.addMarkerDetector(markerType, param1, param2);
}
// ------------------------------------------------------------------------
/**
* Returns the projection matrix. Since the camera doesn't move during tracking,
* this needs to be called only once after init()
to obtain the
* projection matrix.
*
* @return The matrix is retured as a ByteArray
containing 4x4 Numbers.
*
* @example To set the projection matrix for a camera in
* papervison3D,
* you would do the following:
*
* * var mat:ByteArray = flareTrackerTracker.getProjectionMatrix(); * var proj:Matrix3D = (_camera as Camera3D).projection; * * proj.n11 = mat.readFloat(); * proj.n21 = -mat.readFloat(); * proj.n31 = mat.readFloat(); * proj.n41 = mat.readFloat(); * * proj.n12 = mat.readFloat(); * proj.n22 = -mat.readFloat(); * proj.n32 = mat.readFloat(); * proj.n42 = mat.readFloat(); * * proj.n13 = mat.readFloat(); * proj.n23 = -mat.readFloat(); * proj.n33 = mat.readFloat(); * proj.n43 = mat.readFloat(); * * proj.n14 = mat.readFloat(); * proj.n24 = -mat.readFloat(); * proj.n34 = mat.readFloat(); * proj.n44 = mat.readFloat(); ** * Note that the 2nd row is inverted, because we need to convert from a * right-handed coordinate system (used by flare) to a left-handed * coordinate system (used by * papervison3D). */ public function getProjectionMatrix():ByteArray { if (! m_alcMemory) return null; m_alcMemory.position = m_trackerLib.getProjectionMatrixPtr(); return m_alcMemory; } // ------------------------------------------------------------------------ /** * This method needs to be called every frame to obtain the tracking results. * * @param image The bitmap grabbed from the camera. * * @return Number of targets found. */ public function update(image:BitmapData):uint { if (! m_alcMemory) return 0; // write to "alchemy memory" m_alcMemory.position = m_alcImagePointer; m_alcMemory.writeBytes(image.getPixels(image.rect)); // returns number of targets found return (m_trackerLib.update()); } // ------------------------------------------------------------------------ /** * Returns the tracking results. Call this method after
update()
* found one or more targets.
*
* @return The tracking results are returned as a ByteArray
structure
* of the following form:
* * markerType:int; // type of marker, can be one of the following: * // MARKER_SIMPLE_ID * // MARKER_BCH * // MARKER_BCH2 * // MARKER_FRAME * // MARKER_SPLIT * // MARKER_DATAMATRIX * targetID:int; // unique identifier of the target * * poseMatrix_11:Number; // pose matrix: model view matrix of target in 3D space * poseMatrix_21:Number; * poseMatrix_31:Number; * poseMatrix_41:Number; * * poseMatrix_12:Number; * poseMatrix_22:Number; * poseMatrix_32:Number; * poseMatrix_42:Number; * * poseMatrix_13:Number; * poseMatrix_23:Number; * poseMatrix_33:Number; * poseMatrix_43:Number; * * poseMatrix_14:Number; * poseMatrix_24:Number; * poseMatrix_34:Number; * poseMatrix_44:Number; ** This structure is repeated in the
ByteArray
for every target found.
*
* @example This example shows how the tracker results can be parsed.
* * var markerType:int; * var targetID:int; * var mat:Matrix3D = new Matrix3D(); * var numTargets:uint = flareTrackerTracker.update(bitmap); * var targetData:ByteArray = flareTrackerTracker.getTrackerResults(); * * // iterate over all visible targets * for (var i:uint = 0; i < numTargets; i++) * { * markerType = targetData.readInt(); * targetID = targetData.readInt(); * * // read pose matrix (= model view matrix) * mat.n11 = targetData.readFloat(); * mat.n21 = -targetData.readFloat(); * mat.n31 = targetData.readFloat(); * mat.n41 = targetData.readFloat(); * * mat.n12 = targetData.readFloat(); * mat.n22 = -targetData.readFloat(); * mat.n32 = targetData.readFloat(); * mat.n42 = targetData.readFloat(); * * mat.n13 = targetData.readFloat(); * mat.n23 = -targetData.readFloat(); * mat.n33 = targetData.readFloat(); * mat.n43 = targetData.readFloat(); * * mat.n14 = targetData.readFloat(); * mat.n24 = -targetData.readFloat(); * mat.n34 = targetData.readFloat(); * mat.n44 = targetData.readFloat(); * * // show target object and apply transformation * showObject(markerType, targetID, mat); * } ** * The 2nd row of the pose matrix is inverted to convert from a right-handed * coordinate system to a left-handed coordinate system. */ public function getTrackerResults():ByteArray { if (! m_alcMemory) return null; m_alcMemory.position = m_alcTrackerResultPointer; return m_alcMemory; } // ------------------------------------------------------------------------ /** * Returns the 2d-tracking results. Call this method after
update()
* found one or more targets.
*
* @return The 2d-tracking results are returned as a ByteArray
structure
* of the following form:
* * markerType:int; // type of marker, can be one of the following: * // MARKER_SIMPLE_ID * // MARKER_BCH * // MARKER_BCH2 * // MARKER_FRAME * // MARKER_SPLIT * // MARKER_DATAMATRIX * targetID:int; // unique identifier of the target * * // corner points of the rectangular marker in image space * cornerUL_x:Number; // upper left corner point * cornerUL_y:Number; * * cornerUR_x:Number; // upper right corner point * cornerUR_y:Number; * * cornerLR_x:Number; // lower right corner point * cornerLR_y:Number; * * cornerLL_x:Number; // lower left corner point * cornerLL_y:Number; ** This structure is repeated in the
ByteArray
for every target found.
*
*/
public function getTrackerResults2D():ByteArray
{
if (! m_alcMemory) return null;
m_alcMemory.position = m_alcTrackerResult2DPointer;
return m_alcMemory;
}
// ------------------------------------------------------------------------
/**
* Returns the message encoded in a data matrix marker.
* Call this method after update()
found one or more targets.
*
* @param index Retrieves the message for the data matrix marker at position index
.
* Where index
is smaller than the number of targets returned by
* update()
.
*
* @return Message encoded in data matrix marker, or null
* if no message is found. null
is also returned if
* index
doesn't point to a marker of type data matrix.
*/
public function getDataMatrixMessage(index:uint):String
{
return (m_trackerLib.getDataMatrixMessage(index));
}
// ------------------------------------------------------------------------
private function fileLoadedCB(url:String, data:ByteArray, errMsg:String=null):void
{
try
{
if (errMsg) throw "ERROR: File \"" + url + "\" load failed: " + errMsg;
m_CLib.supplyFile(url, data);
// load targets, after all ini-files have been loaded
if (m_fileLoader.allFilesLoaded())
{
var isOk:Boolean = m_trackerLib.initTracker(m_stage, m_camWidth, m_camHeight, m_camFile);
if (!isOk) throw "ERROR: initTracker() failed.";
// retrieve the "alchemy memory"
var ns : Namespace = new Namespace("cmodule.libFlareTracker");
m_alcMemory = (ns::gstate).ds;
// get offsets to image buffer and tracker-result buffer
m_alcImagePointer = m_trackerLib.getImageBufferPtr();
m_alcTrackerResultPointer = m_trackerLib.getTrackerResultPtr();
m_alcTrackerResult2DPointer = m_trackerLib.getTrackerResult2DPtr();
m_initDoneCB(); // callback function to indicate that init() is finished
}
}
catch (errStr:*)
{
if (m_logFunc != null) m_logFunc(0, String(errStr));
}
}
// ------------------------------------------------------------------------
}
// ----------------------------------------------------------------------------
}
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
// ----------------------------------------------------------------------------
// helper class to load files into memory
class FilePreloader
{
private var m_loadCB:Function;
private var m_ldrArray:Array;
private var m_filesToLoad:uint;
private var m_loadEnd:Boolean;
// ------------------------------------------------------------------------
public function FilePreloader(loadCB:Function)
{
m_loadCB = loadCB;
m_ldrArray = new Array();
m_filesToLoad = 0;
m_loadEnd = false;
}
// ------------------------------------------------------------------------
public function load(filename:String, path:String):void
{
var url:String = path + "/" + filename;
try {
var loader:URLLoader = new URLLoader();
m_ldrArray.push([loader, filename]);
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorCB);
loader.addEventListener(IOErrorEvent.IO_ERROR, errorCB);
loader.addEventListener(Event.COMPLETE, dataCB);
loader.load(new URLRequest(url));
m_filesToLoad++;
}
catch (err:Error) { onError(filename, err.message); }
}
// ------------------------------------------------------------------------
public function loadStart():void { m_loadEnd = false; m_filesToLoad = 0; }
public function loadEnd():void { m_loadEnd = true; }
public function allFilesLoaded():Boolean
{
return (m_loadEnd && (m_filesToLoad == 0));
}
// ------------------------------------------------------------------------
private function onLoad(url:String, data:ByteArray):void
{
m_filesToLoad--;
if (m_loadCB != null) m_loadCB(url, data);
}
// ------------------------------------------------------------------------
private function onError(url:String, errMsg:String):void
{
if (m_loadCB != null) m_loadCB(url, null, errMsg);
}
// ------------------------------------------------------------------------
private function errorCB(event:Event):void
{
loadHandler(URLLoader(event.target), event.type);
}
// ------------------------------------------------------------------------
private function dataCB(event:Event):void
{
loadHandler(URLLoader(event.target));
}
// ------------------------------------------------------------------------
private function loadHandler(loader:URLLoader, errMsg:String = null):void
{
for (var i:uint = 0; i < m_ldrArray.length; i++)
{
if (m_ldrArray[i][0] == loader)
{
var url:String = m_ldrArray[i][1];
if (errMsg == null) onLoad (url, loader.data);
else onError(url, errMsg);
m_ldrArray.splice(i, 1); // removes element at index i
return;
}
}
// should never get here
onError("", "FilePreloader: load error!");
}
// ------------------------------------------------------------------------
}