PageRenderTime 3229ms CodeModel.GetById 39ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/transmote/flar/FLARManager.as

http://github.com/tcha-tcho/EZFLAR
ActionScript | 1154 lines | 709 code | 149 blank | 296 comment | 111 complexity | 7eca3bd6f27c3f85014139fd644a529a MD5 | raw file
  1. /*
  2. * PROJECT: FLARManager
  3. * http://transmote.com/flar
  4. * Copyright 2009, Eric Socolofsky
  5. * --------------------------------------------------------------------------------
  6. * This work complements FLARToolkit, developed by Saqoosha as part of the Libspark project.
  7. * http://www.libspark.org/wiki/saqoosha/FLARToolKit
  8. * FLARToolkit is Copyright (C)2008 Saqoosha,
  9. * and is ported from NYARToolkit, which is ported from ARToolkit.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this framework; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24. *
  25. * For further information please contact:
  26. * <eric(at)transmote.com>
  27. * http://transmote.com/flar
  28. *
  29. */
  30. package com.transmote.flar {
  31. import __AS3__.vec.Vector;
  32. import com.transmote.flar.marker.FLARMarker;
  33. import com.transmote.flar.marker.FLARMarkerEvent;
  34. import com.transmote.flar.pattern.FLARPattern;
  35. import com.transmote.flar.pattern.FLARPatternLoader;
  36. import com.transmote.flar.source.FLARCameraSource;
  37. import com.transmote.flar.source.FLARLoaderSource;
  38. import com.transmote.flar.source.IFLARSource;
  39. import com.transmote.flar.utils.FLARManagerConfigLoader;
  40. import com.transmote.flar.utils.FLARProxy;
  41. import com.transmote.flar.utils.smoother.FLARMatrixSmoother_Average;
  42. import com.transmote.flar.utils.smoother.IFLARMatrixSmoother;
  43. import com.transmote.flar.utils.threshold.DrunkHistogramThresholdAdapter;
  44. import com.transmote.flar.utils.threshold.IThresholdAdapter;
  45. import flash.display.Bitmap;
  46. import flash.display.BitmapData;
  47. import flash.display.DisplayObject;
  48. import flash.display.DisplayObjectContainer;
  49. import flash.display.Shader;
  50. import flash.display.Sprite;
  51. import flash.events.ErrorEvent;
  52. import flash.events.Event;
  53. import flash.events.EventDispatcher;
  54. import flash.events.IOErrorEvent;
  55. import flash.events.SecurityErrorEvent;
  56. import flash.filters.BlurFilter;
  57. import flash.filters.ShaderFilter;
  58. import flash.geom.Point;
  59. import flash.geom.Rectangle;
  60. import flash.net.URLLoader;
  61. import flash.net.URLLoaderDataFormat;
  62. import flash.net.URLRequest;
  63. import flash.utils.ByteArray;
  64. import flash.utils.getQualifiedClassName;
  65. import jp.nyatla.nyartoolkit.as3.core.squaredetect.NyARSquare;
  66. import org.libspark.flartoolkit.FLARException;
  67. import org.libspark.flartoolkit.core.param.FLARParam;
  68. import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData;
  69. import org.libspark.flartoolkit.core.transmat.FLARTransMatResult;
  70. import org.libspark.flartoolkit.detector.FLARMultiMarkerDetector;
  71. use namespace flarManagerInternal;
  72. /**
  73. * <p>
  74. * Manager for computer vision applications using FLARToolkit
  75. * (<a href="http://www.libspark.org/wiki/saqoosha/FLARToolKit/en" target="_blank">
  76. * http://www.libspark.org/wiki/saqoosha/FLARToolKit/en</a>).
  77. * </p>
  78. * <p>
  79. * Basic usage is as follows:
  80. * Pass a path to a camera parameters file and a list of FLARPatterns to the constructor.
  81. * Optionally pass an IFLARSource to use as the source image for marker detection;
  82. * FLARManager will by default create a FLARCameraSource that uses the first available camera.
  83. * Alternatively, FLARManager can be initialized using an xml file that specifies the above and other settings.
  84. * </p>
  85. * <p>
  86. * Assign event listeners to FLARManager for MARKER_ADDED,
  87. * MARKER_UPDATED, and MARKER_REMOVED FLARMarkerEvents.
  88. * These FLARMarkerEvents encapsulate the FLARMarker instances that they refer to.
  89. * Alternatively, it is possible to retrieve all active markers
  90. * directly from FLARManager, via FLARManager.activeMarkers.
  91. * </p>
  92. * <p>
  93. * FLARMarkers are simple objects that contain information about detected markers
  94. * provided by FLARToolkit. FLARManager maintains a list of active markers,
  95. * and updates the list and the markers within every frame.
  96. * </p>
  97. *
  98. * @author Eric Socolofsky
  99. * @url http://transmote.com/flar
  100. * @see com.transmote.flar.marker.FLARMarkerEvent
  101. * @see com.transmote.flar.source.FLARCameraSource
  102. * @see com.transmote.flar.utils.FLARProxy
  103. */
  104. public class FLARManager extends EventDispatcher {
  105. private static const ZERO_POINT:Point = new Point();
  106. // general management
  107. private var configLoader:FLARManagerConfigLoader;
  108. private var _activeMarkers:Vector.<FLARMarker>;
  109. private var _cameraParams:FLARParam;
  110. private var _flarSource:IFLARSource;
  111. // source and detection adjustment
  112. private var _threshold:Number = 80;
  113. private var _sampleBlurring:int = 1;
  114. private var _thresholdAdapter:IThresholdAdapter;
  115. private var _inverted:Boolean;
  116. private var _mirrorDisplay:Boolean = true;
  117. // marker adjustment
  118. private var _markerUpdateThreshold:Number = 80;
  119. private var _markerRemovalDelay:int = 1;
  120. private var _markerExtrapolation:Boolean = true;
  121. private var _smoothing:int = 3;
  122. private var _smoother:IFLARMatrixSmoother;
  123. private var _adaptiveSmoothingCenter:Number = 10;
  124. // debugging
  125. private var _thresholdSourceDisplay:Boolean;
  126. // pattern and marker management
  127. private var patternLoader:FLARPatternLoader;
  128. private var allPatterns:Vector.<FLARPattern>;
  129. private var markerDetector:FLARMultiMarkerDetector;
  130. private var flarRaster:FLARRgbRaster_BitmapData;
  131. private var thresholdSourceBitmap:Bitmap;
  132. private var markersPendingRemoval:Vector.<FLARMarker>;
  133. // marker adjustment (private)
  134. private var enterframer:Sprite;
  135. private var averageConfidence:Number = FLARPattern.DEFAULT_MIN_CONFIDENCE;
  136. private var averageMinConfidence:Number = FLARPattern.DEFAULT_MIN_CONFIDENCE;
  137. private var sampleBlurFilter:BlurFilter;
  138. private var inversionShaderFilter:ShaderFilter;
  139. // application state
  140. private var bInited:Boolean;
  141. private var bCameraParamsLoaded:Boolean;
  142. private var bPatternsLoaded:Boolean;
  143. private var bActive:Boolean;
  144. private var bVerbose:Boolean;
  145. /**
  146. * Constructor.
  147. * Initialize FLARManager by passing in a configuration file path.
  148. *
  149. * @param flarConfigPath path to the FLARManager configuration xml file.
  150. */
  151. public function FLARManager (flarConfigPath:String="") {
  152. this._flarSource = new FLARCameraSource();
  153. if (flarConfigPath != "") {
  154. this.initFromFile(flarConfigPath);
  155. }
  156. }
  157. /**
  158. * the old-fashioned way to initialize FLARManager.
  159. * the preferred method is to use an external xml configuration file,
  160. * and to pass its path into the FLARManager constructor.
  161. *
  162. * @param cameraParamsPath camera parameters filename.
  163. * @param patterns list of FLARPatterns to detect.
  164. * @param source IFLARSource instance to use as source image for marker detection.
  165. * if null, FLARManager will create a camera capture source.
  166. */
  167. public function initManual (cameraParamsPath:String, patterns:Vector.<FLARPattern>, source:IFLARSource=null) :void {
  168. use namespace flarManagerInternal;
  169. if (source) {
  170. this._flarSource = source;
  171. this._mirrorDisplay = source.mirrored;
  172. } else {
  173. this._flarSource = new FLARCameraSource();
  174. }
  175. this.initConfigLoader();
  176. this.configLoader.cameraParamsPath = cameraParamsPath;
  177. /*
  178. FIXME this is not good: Vectors are 62% slower than Arrays :{ | http://www.mikechambers.com/blog/2008/09/24/actioscript-3-vector-array-performance-comparison/
  179. */
  180. this.configLoader.patterns = patterns;
  181. this.init();
  182. }
  183. /**
  184. * load FLARManager configuration from an xml file.
  185. * note, this method no longer needs to be called manually;
  186. * simply passing the configuration file path into the constructor
  187. * will also initialize correctly. this method is here for legacy support only.
  188. *
  189. * @param flarConfigPath path to the FLARManager configuration xml file.
  190. */
  191. public function initFromFile (flarConfigPath:String) :void {
  192. use namespace flarManagerInternal;
  193. if (!this.configLoader) { this.initConfigLoader(); }
  194. this.configLoader.loadConfigFile(flarConfigPath);
  195. }
  196. /**
  197. * init from an XML object in Flash.
  198. * this can be useful when dynamically generating an XML file at runtime,
  199. * such as via a middleware request.
  200. */
  201. public function initFromXML (flarConfigXML:XML) :void {
  202. if (!this.configLoader) { this.initConfigLoader(); }
  203. this.configLoader.parseConfigFile(flarConfigXML);
  204. }
  205. //-----<CONFIG ACCESSORS>------------------------------------//
  206. /**
  207. * pixels in source image with a brightness <= to this.threshold are candidates for marker outline detection.
  208. * increase to increase likelihood of marker detection;
  209. * increasing too high will cause engine to incorrectly detect non-existent markers.
  210. * defaults to 80 (values can range from 0 to 255).
  211. */
  212. public function get threshold () :int {
  213. return this._threshold;
  214. }
  215. public function set threshold (val:int) :void {
  216. if (this.bVerbose && !this.thresholdAdapter) {
  217. trace("[EZFLAR::FM] threshold: "+ val);
  218. }
  219. this._threshold = val;
  220. }
  221. /**
  222. * IFLARThresholdAdapter used to automate threshold changes.
  223. *
  224. * adaptive thresholding can result in better marker detection across a range of illumination.
  225. * this is desirable for applications with low lighting, or in which the developer has little control
  226. * over lighting conditions, such as with web applications.
  227. *
  228. * note that adaptive thresholding may cause slower performance in very dark environments.
  229. * this happens because a low threshold creates an image with large black areas,
  230. * and images with a lot of black can cause slowdown in FLARToolkit's labeling process
  231. * (FLARLabeling_BitmapData.labeling()). in this case, thresholdAdapter should be set to null.
  232. *
  233. * the default threshold adapter is DrunkHistogramThresholdAdapter, but developers can implement their own
  234. * algorithms for adaptive thresholding. to do so, implement the IThresholdAdapter interface.
  235. */
  236. public function get thresholdAdapter () :IThresholdAdapter {
  237. return this._thresholdAdapter;
  238. }
  239. public function set thresholdAdapter (val:IThresholdAdapter) :void {
  240. if (this.bVerbose) {
  241. trace("[EZFLAR::FM] threshold adapter: "+ flash.utils.getQualifiedClassName(val));
  242. }
  243. this._thresholdAdapter = val;
  244. }
  245. /**
  246. * the amount of blur applied to the source image
  247. * before sending to FLARToolkit for marker detection.
  248. * higher values increase framerate, but reduce detection accuracy.
  249. * value must be zero or greater. the default value is 2.
  250. */
  251. public function get sampleBlurring () :int {
  252. return this._sampleBlurring;
  253. }
  254. public function set sampleBlurring (val:int) :void {
  255. if (!this.sampleBlurFilter) {
  256. this.sampleBlurFilter = new BlurFilter();
  257. }
  258. if (val <= 0) {
  259. val = 0;
  260. this.sampleBlurFilter.blurX = this.sampleBlurFilter.blurY = 0;
  261. } else {
  262. this.sampleBlurFilter.blurX = this.sampleBlurFilter.blurY = Math.pow(2, val-1);
  263. }
  264. this._sampleBlurring = val;
  265. if (this.bVerbose) {
  266. trace("[EZFLAR::FM] sample blurring: "+ val);
  267. }
  268. }
  269. /**
  270. * set to true to detect inverted (white border) markers.
  271. * if inverted has not yet been set to true in this session,
  272. * there will be a slight delay before inverted becomes true,
  273. * while the inversion shader is loaded.
  274. *
  275. * thanks to jim alliban (http://jamesalliban.wordpress.com/)
  276. * and lee brimelow (http://theflashblog.com/) for the inversion algorithm.
  277. */
  278. public function get inverted () :Boolean {
  279. return this._inverted;
  280. }
  281. public function set inverted (val:Boolean) :void {
  282. if (val && !this._inverted && !this.inversionShaderFilter) {
  283. this.initInversionShader();
  284. return;
  285. }
  286. if (this.bVerbose) {
  287. trace("[EZFLAR::FM] inverted: "+ val);
  288. }
  289. this._inverted = val;
  290. }
  291. /**
  292. * set to true to flip the camera image horizontally;
  293. * this value is passed on to this.flarSource;
  294. * note that if an IFLARSource is specified after mirrorDisplay is set,
  295. * the 'mirrored' property of the new IFLARSource will overwrite this value.
  296. */
  297. public function get mirrorDisplay () :Boolean {
  298. return this._mirrorDisplay;
  299. }
  300. public function set mirrorDisplay (val:Boolean) :void {
  301. if (this.bVerbose) {
  302. trace("[EZFLAR::FM] mirror display: "+ val);
  303. }
  304. this._mirrorDisplay = val;
  305. if (this.flarSource) {
  306. this.flarSource.mirrored = this._mirrorDisplay;
  307. }
  308. }
  309. /**
  310. * provides direct access to FLARLabeling_BitmapData.minimumLabelSize,
  311. * which is the minimum size (width*height) a dark area of the source image must be
  312. * in order to become a candidate for marker outline detection.
  313. * higher values result in faster performance,
  314. * but poorer marker detection at smaller sizes (as they appear on-screen).
  315. * defaults to 100.
  316. */
  317. public function get minimumLabelSize () :Number {
  318. /*
  319. return FLARLabeling_BitmapData.minimumLabelSize;
  320. */
  321. return -1;
  322. }
  323. public function set minimumLabelSize (val:Number) :void {
  324. /*
  325. if (this.bVerbose) {
  326. trace("[EZFLAR::FM] minimum label size: "+ val);
  327. }
  328. FLARLabeling_BitmapData.minimumLabelSize = val;
  329. */
  330. }
  331. /**
  332. * if a detected marker is within this distance (pixels) from an active marker,
  333. * FLARManager considers the detected marker to be an update of the active marker.
  334. * else, the detected marker is a new marker.
  335. * increase this value to accommodate faster-moving markers;
  336. * decrease it to accommodate more markers on-screen at once.
  337. */
  338. public function get markerUpdateThreshold () :Number {
  339. return this._markerUpdateThreshold;
  340. }
  341. public function set markerUpdateThreshold (val:Number) :void {
  342. if (this.bVerbose) {
  343. trace("[EZFLAR::FM] marker update threshold: "+ val);
  344. }
  345. this._markerUpdateThreshold = val;
  346. }
  347. /**
  348. * number of frames after removal that a marker will persist before dispatching a MARKER_REMOVED event.
  349. * if a marker of the same pattern appears within <tt>markerUpdateThreshold</tt> pixels
  350. * of the point of removal, before <tt>markerRemovalDelay</tt> frames have elapsed,
  351. * the marker will be reinstated as if it was never removed.
  352. *
  353. * the default value is 1.
  354. */
  355. public function get markerRemovalDelay () :int {
  356. return this._markerRemovalDelay;
  357. }
  358. public function set markerRemovalDelay (val:int) :void {
  359. if (this.bVerbose) {
  360. trace("[EZFLAR::FM] marker removal delay: "+ val);
  361. }
  362. this._markerRemovalDelay = val;
  363. }
  364. /**
  365. * if true, FLARManager will extrapolate the position of a FLARMarker from its velocity when last detected.
  366. * extrapolation continues until markerRemovalDelay frames after FLARToolkit reports a marker removed.
  367. * enabling markerExtrapolation can sometimes improve tracking during fast marker motion.
  368. */
  369. public function get markerExtrapolation () :Boolean {
  370. return this._markerExtrapolation;
  371. }
  372. public function set markerExtrapolation (val:Boolean) :void {
  373. if (this.bVerbose) {
  374. trace("[EZFLAR::FM] marker extrapolation: "+ (val ? "ON" : "OFF"));
  375. }
  376. this._markerExtrapolation = val;
  377. }
  378. /**
  379. * apply a smoothing algorithm to transformation matrices generated by FLARToolkit.
  380. * smoothing is equal to the number of frames over which FLARManager
  381. * will average transformation matrices; the larger the number, the smoother the animation,
  382. * and the slower the response time between marker position/orientation changes.
  383. * a value of 0 turns smoothing off.
  384. */
  385. public function get smoothing () :int {
  386. return this._smoothing;
  387. }
  388. public function set smoothing (val:int) :void {
  389. if (this.bVerbose) {
  390. trace("[EZFLAR::FM] smoothing: "+ val);
  391. }
  392. this._smoothing = val;
  393. }
  394. /**
  395. * IFLARMatrixSmoother to use to apply smoothing to transformation matrices generated by FLARToolkit.
  396. */
  397. public function get smoother () :IFLARMatrixSmoother {
  398. return this._smoother;
  399. }
  400. public function set smoother (val:IFLARMatrixSmoother) :void {
  401. if (this.bVerbose) {
  402. trace("[EZFLAR::FM] smoother "+ flash.utils.getQualifiedClassName(val));
  403. }
  404. this._smoother = val;
  405. }
  406. /**
  407. * adaptive smoothing reduces jitter in marker transformation matrices for markers with
  408. * little motion, while maintaining responsiveness for markers with fast motion.
  409. * adaptiveSmoothingCenter is the marker motion distance (between current and last frame)
  410. * at which the actual applied smoothing is equal to FLARManager.smoothing.
  411. * when a marker has moved less than adaptiveSmoothingCenter, smoothing increases;
  412. * when a marker has moved more than adaptiveSmoothingCenter, smoothing decreases.
  413. */
  414. public function get adaptiveSmoothingCenter () :Number {
  415. return this._adaptiveSmoothingCenter;
  416. }
  417. public function set adaptiveSmoothingCenter (val:Number) :void {
  418. if (this.bVerbose) {
  419. trace("[EZFLAR::FM] adaptive smoothing center: "+ val);
  420. }
  421. this._adaptiveSmoothingCenter = val;
  422. }
  423. /**
  424. * display the source BitmapData used by FLARToolkit post-thresholding.
  425. * useful for debugging.
  426. */
  427. public function get thresholdSourceDisplay () :Boolean {
  428. return this._thresholdSourceDisplay;
  429. }
  430. public function set thresholdSourceDisplay (val:Boolean) :void {
  431. if (this.bVerbose) {
  432. trace("[EZFLAR::FM] threshold source display: "+ val);
  433. }
  434. this._thresholdSourceDisplay = val;
  435. if (this._thresholdSourceDisplay) {
  436. try {
  437. if (!this.thresholdSourceBitmap) {
  438. if (!this.markerDetector.thresholdedBitmapData) {
  439. throw new Error("Error initializing FLARMultiMarkerDetector; thresholdedBitmapData not inited.");
  440. }
  441. this.thresholdSourceBitmap = new Bitmap(this.markerDetector.thresholdedBitmapData);
  442. Sprite(this._flarSource).addChild(this.thresholdSourceBitmap);
  443. }
  444. } catch (e:Error) {
  445. this.thresholdSourceBitmap = null;
  446. return;
  447. }
  448. }
  449. }
  450. //-----<END CONFIG ACCESSORS>--------------------------------//
  451. //-----<OTHER ACCESSORS>-------------------------------------//
  452. /**
  453. * FLARParam used by this FLARManager.
  454. * can be used to instantiate a FLARCamera3D for use with Papervision.
  455. */
  456. public function get cameraParams () :FLARParam {
  457. return this._cameraParams;
  458. }
  459. /**
  460. * IFLARSource instance this FLARManager is using as the source image for marker detection.
  461. */
  462. public function get flarSource () :IFLARSource {
  463. return this._flarSource;
  464. }
  465. /**
  466. * reference to FLARCameraSource used in this application.
  467. * if this application does not use a camera, returns null.
  468. */
  469. public function get flarCameraSource () :FLARCameraSource {
  470. return this._flarSource as FLARCameraSource;
  471. }
  472. /**
  473. * the number of patterns loaded for detection.
  474. */
  475. public function get numLoadedPatterns () :int {
  476. return this.patternLoader.loadedPatterns.length;
  477. }
  478. /**
  479. * Vector of all currently-active markers.
  480. */
  481. public function get activeMarkers () :Vector.<FLARMarker> {
  482. return this._activeMarkers;
  483. }
  484. /**
  485. * true if this FLARManager instance is active and currently processing marker detection.
  486. */
  487. public function get isActive () :Boolean {
  488. return this.bActive;
  489. }
  490. public function set isActive (val:Boolean) :void {
  491. trace("[EZFLAR::FM] "+ (val ? "activated" : "deactivated"));
  492. if (val) {
  493. this.activate();
  494. } else {
  495. this.deactivate();
  496. }
  497. }
  498. /**
  499. * if true, FLARManager will output configuration changes to the console.
  500. */
  501. public function get verbose () :Boolean {
  502. return this.bVerbose;
  503. }
  504. public function set verbose (val:Boolean) :void {
  505. this.bVerbose = val;
  506. trace("[EZFLAR::FM] verbosity "+ (val ? "ON" : "OFF"));
  507. }
  508. //-----<END OTHER ACCESSORS>---------------------------------//
  509. //-----<PUBLIC METHODS>----------------------------//
  510. /**
  511. * begin detecting markers once per frame.
  512. * this method is called automatically on initialization.
  513. * @return false if FLARManager is not yet initialized; else true.
  514. */
  515. public function activate () :Boolean {
  516. if (!this.bInited) { return false; }
  517. if (this.bActive) { return true; }
  518. this.bActive = true;
  519. if (this._flarSource is FLARProxy) {
  520. // activate FLARProxy
  521. var flarProxy:FLARProxy = this._flarSource as FLARProxy;
  522. flarProxy.activate();
  523. flarProxy.addEventListener(FLARMarkerEvent.MARKER_ADDED, this.onProxyMarkerAdded);
  524. flarProxy.addEventListener(FLARMarkerEvent.MARKER_UPDATED, this.onProxyMarkerUpdated);
  525. flarProxy.addEventListener(FLARMarkerEvent.MARKER_REMOVED, this.onProxyMarkerRemoved);
  526. } else {
  527. // activate normally
  528. if (!this.enterframer) {
  529. this.enterframer = new Sprite();
  530. }
  531. this.enterframer.removeEventListener(Event.ENTER_FRAME, this.onEnterFrame);
  532. this.enterframer.addEventListener(Event.ENTER_FRAME, this.onEnterFrame, false, 0, true);
  533. }
  534. this._activeMarkers = new Vector.<FLARMarker>();
  535. this.markersPendingRemoval = new Vector.<FLARMarker>();
  536. return true;
  537. }
  538. /**
  539. * stop detecting markers.
  540. * removes all currently-active markers.
  541. * enterframe loop continues, to update video.
  542. */
  543. public function deactivate () :void {
  544. if (!this.bActive) {
  545. return;
  546. }
  547. this.bActive = false;
  548. if (this._flarSource is FLARProxy) {
  549. // deactivate FLARProxy
  550. var flarProxy:FLARProxy = this._flarSource as FLARProxy;
  551. flarProxy.deactivate();
  552. flarProxy.addEventListener(FLARMarkerEvent.MARKER_ADDED, this.onProxyMarkerAdded);
  553. flarProxy.addEventListener(FLARMarkerEvent.MARKER_UPDATED, this.onProxyMarkerUpdated);
  554. flarProxy.addEventListener(FLARMarkerEvent.MARKER_REMOVED, this.onProxyMarkerRemoved);
  555. }
  556. if (this._activeMarkers) {
  557. var i:int = this._activeMarkers.length;
  558. while (i--) {
  559. // remove all active markers
  560. this.removeMarker(this._activeMarkers[i]);
  561. }
  562. this._activeMarkers = null;
  563. }
  564. if (this.markersPendingRemoval) {
  565. i = this.markersPendingRemoval.length;
  566. while (i--) {
  567. this.markersPendingRemoval[i].dispose();
  568. }
  569. this.markersPendingRemoval = null;
  570. }
  571. }
  572. /**
  573. * halts all processes and frees this instance for garbage collection.
  574. */
  575. public function dispose () :void {
  576. this.deactivate();
  577. this.disposeConfigLoader();
  578. this.enterframer.removeEventListener(Event.ENTER_FRAME, this.onEnterFrame);
  579. this.enterframer = null;
  580. this._cameraParams = null;
  581. this._flarSource.dispose();
  582. var flarSourceDO:DisplayObject = this._flarSource as DisplayObject;
  583. if (flarSourceDO && flarSourceDO.parent) {
  584. flarSourceDO.parent.removeChild(flarSourceDO);
  585. }
  586. this._flarSource = null;
  587. this._thresholdAdapter.dispose();
  588. this._thresholdAdapter = null;
  589. this._smoother = null;
  590. this.patternLoader.dispose();
  591. this.patternLoader = null;
  592. this.allPatterns = null;
  593. // NOTE: FLARToolkit classes do not implement any disposal functionality,
  594. // and will likely not be removed from memory on FLARManager disposal.
  595. //this.markerDetector.dispose();
  596. this.markerDetector = null;
  597. BitmapData(this.flarRaster.getBuffer()).dispose();
  598. this.flarRaster = null;
  599. if (this.thresholdSourceBitmap) {
  600. this.thresholdSourceBitmap.bitmapData.dispose();
  601. }
  602. this.thresholdSourceBitmap = null;
  603. this.sampleBlurFilter = null;
  604. }
  605. //-----<END PUBLIC METHODS>---------------------------//
  606. //-----<MARKER DETECTION>----------------------------//
  607. private function onEnterFrame (evt:Event) :void {
  608. /*
  609. FIXME updateSource() test is not passing... this will not fire detection
  610. */
  611. if (!this.updateSource()) { return; }
  612. if (!this.bActive) { return; }
  613. this.ageRemovedMarkers();
  614. this.performSourceAdjustments();
  615. this.detectMarkers();
  616. }
  617. private function updateSource () :Boolean {
  618. try {
  619. // ensure this.flarRaster has been initialized
  620. if (this.flarRaster == null) {
  621. /*
  622. FIXME flarRaster is not working ... is getting a error ... someone here is null
  623. */
  624. //this.flarRaster = new FLARRgbRaster_BitmapData(this.flarSource.sourceSize.width, this.flarSource.sourceSize.height);
  625. this.flarRaster = new FLARRgbRaster_BitmapData(this.thresholdSourceBitmap.bitmapData);
  626. //this.flarSource.source = BitmapData(this.flarRaster.getBuffer());
  627. }
  628. } catch (e:Error) {
  629. // this.flarSource not yet fully initialized
  630. return false;
  631. }
  632. // update source image
  633. this.flarSource.update();
  634. return true;
  635. }
  636. private function ageRemovedMarkers () :void {
  637. // remove all markers older than this.markerRemovalDelay.
  638. var i:uint = this.markersPendingRemoval.length;
  639. var removedMarker:FLARMarker;
  640. while (i--) {
  641. removedMarker = this.markersPendingRemoval[i];
  642. if (removedMarker.ageAfterRemoval() > this.markerRemovalDelay) {
  643. this.removeMarker(removedMarker);
  644. }
  645. }
  646. }
  647. private function performSourceAdjustments () :void {
  648. if (this.thresholdAdapter) {
  649. if (this.thresholdAdapter.runsEveryFrame) {
  650. // adjust threshold every frame.
  651. this.threshold = this.thresholdAdapter.calculateThreshold(this.flarSource.source, this.threshold);
  652. } else {
  653. // adjust threshold only when confidence is low (poor marker detection).
  654. if (this.averageConfidence <= this.averageMinConfidence) {
  655. this.threshold = this.thresholdAdapter.calculateThreshold(this.flarSource.source, this.threshold);
  656. } else {
  657. this.thresholdAdapter.resetCalculations(this.threshold);
  658. }
  659. }
  660. this.averageConfidence = this.averageMinConfidence = 0;
  661. }
  662. if (this.sampleBlurring > 0) {
  663. // apply blur filter to combine and reduce number of black areas in image to be labeled.
  664. this.flarSource.source.applyFilter(this.flarSource.source, this.flarSource.sourceSize, ZERO_POINT, this.sampleBlurFilter);
  665. }
  666. if (this._inverted) {
  667. this.flarSource.source.applyFilter(this.flarSource.source, this.flarSource.sourceSize, ZERO_POINT, this.inversionShaderFilter);
  668. }
  669. }
  670. private function detectMarkers () :void {
  671. trace("detectMarkers");
  672. var numFoundMarkers:int = 0;
  673. try {
  674. // detect marker(s)
  675. numFoundMarkers = this.markerDetector.detectMarkerLite(this.flarRaster, this.threshold);
  676. } catch (e:FLARException) {
  677. // error in FLARToolkit processing; send to console
  678. trace(e);
  679. return;
  680. }
  681. var activeMarker:FLARMarker;
  682. var i:uint;
  683. if (numFoundMarkers == 0) {
  684. // if no markers found, remove any existing markers and exit
  685. i = this._activeMarkers.length;
  686. while (i--) {
  687. this.queueMarkerForRemoval(this._activeMarkers[i]);
  688. }
  689. return;
  690. }
  691. // build list of detected markers
  692. var detectedMarkers:Vector.<FLARMarker> = new Vector.<FLARMarker>();
  693. // var detectedMarkerResult:FLARMultiMarkerDetectorResult;
  694. var patternId:int;
  695. var direction:int;
  696. var square:NyARSquare;
  697. var patternIndex:int;
  698. var detectedPattern:FLARPattern;
  699. var confidence:Number;
  700. var confidenceSum:Number = 0;
  701. var minConfidenceSum:Number = 0;
  702. var transmat:FLARTransMatResult;
  703. i = numFoundMarkers;
  704. while (i--) {
  705. // detectedMarkerResult = this.markerDetector.getResult(i);
  706. patternId = this.markerDetector.getARCodeIndex(i);
  707. direction = this.markerDetector.getDirection(i);
  708. square = this.markerDetector.getSquare(i);
  709. patternIndex = this.markerDetector.getARCodeIndex(i);
  710. detectedPattern = this.allPatterns[patternIndex];
  711. confidence = this.markerDetector.getConfidence(i);
  712. confidenceSum += confidence;
  713. minConfidenceSum += detectedPattern.minConfidence;
  714. if (confidence < detectedPattern.minConfidence) {
  715. // detected marker's confidence is below the minimum required confidence for its pattern.
  716. continue;
  717. }
  718. transmat = new FLARTransMatResult();
  719. try {
  720. this.markerDetector.getTransformMatrix(i, transmat);
  721. } catch (e:Error) {
  722. // FLARException happens with rotationX of approx -60 and +60, and rotY&Z of 0.
  723. // not sure why...
  724. continue;
  725. }
  726. // detectedMarkers.push(new FLARMarker(detectedMarkerResult, transmat, this.flarSource, detectedPattern));
  727. detectedMarkers.push(new FLARMarker(transmat, this.flarSource, detectedPattern, patternId, direction, square, confidence));
  728. }
  729. this.averageConfidence = confidenceSum / numFoundMarkers;
  730. this.averageMinConfidence = minConfidenceSum / numFoundMarkers;
  731. // compare detected markers against active markers
  732. i = detectedMarkers.length;
  733. var j:uint, k:uint;
  734. var detectedMarker:FLARMarker;
  735. var closestMarker:FLARMarker;
  736. var closestDist:Number = Number.POSITIVE_INFINITY;
  737. var dist:Number;
  738. var updatedMarkers:Vector.<FLARMarker> = new Vector.<FLARMarker>();
  739. var newMarkers:Vector.<FLARMarker> = new Vector.<FLARMarker>();
  740. var removedMarker:FLARMarker;
  741. var bRemovedMarkerMatched:Boolean = false;
  742. while (i--) {
  743. j = this._activeMarkers.length;
  744. detectedMarker = detectedMarkers[i];
  745. closestMarker = null;
  746. closestDist = Number.POSITIVE_INFINITY;
  747. while (j--) {
  748. activeMarker = this._activeMarkers[j];
  749. if (detectedMarker.patternId == activeMarker.patternId) {
  750. dist = Point.distance(detectedMarker.centerpoint3D, activeMarker.targetCenterpoint3D);
  751. if (dist < closestDist && dist < this._markerUpdateThreshold) {
  752. closestMarker = activeMarker;
  753. closestDist = dist;
  754. }
  755. }
  756. }
  757. if (closestMarker) {
  758. // updated marker
  759. closestMarker.copy(detectedMarker);
  760. detectedMarker.dispose();
  761. if (this._smoothing) {
  762. if (!this._smoother) {
  763. // TODO: log as a WARN-level error
  764. trace("no smoother set; specify FLARManager.smoother to enable smoothing.");
  765. } else {
  766. closestMarker.applySmoothing(this._smoother, this._smoothing, this._adaptiveSmoothingCenter);
  767. }
  768. }
  769. updatedMarkers.push(closestMarker);
  770. // if closestMarker is pending removal, restore it.
  771. k = this.markersPendingRemoval.length;
  772. while (k--) {
  773. if (this.markersPendingRemoval[k] == closestMarker) {
  774. closestMarker.resetRemovalAge();
  775. this.markersPendingRemoval.splice(k, 1);
  776. }
  777. }
  778. this.dispatchEvent(new FLARMarkerEvent(FLARMarkerEvent.MARKER_UPDATED, closestMarker));
  779. } else {
  780. // new marker
  781. newMarkers.push(detectedMarker);
  782. detectedMarker.setSessionId();
  783. this.dispatchEvent(new FLARMarkerEvent(FLARMarkerEvent.MARKER_ADDED, detectedMarker));
  784. }
  785. }
  786. i = this._activeMarkers.length;
  787. while (i--) {
  788. activeMarker = this._activeMarkers[i];
  789. if (updatedMarkers.indexOf(activeMarker) == -1) {
  790. // if activeMarker was not updated, queue it for removal.
  791. this.queueMarkerForRemoval(activeMarker);
  792. }
  793. }
  794. this._activeMarkers = this._activeMarkers.concat(newMarkers);
  795. }
  796. private function queueMarkerForRemoval (marker:FLARMarker) :void {
  797. if (this.markersPendingRemoval.indexOf(marker) == -1) {
  798. this.markersPendingRemoval.push(marker);
  799. }
  800. }
  801. private function removeMarker (marker:FLARMarker) :void {
  802. var i:uint = this._activeMarkers.indexOf(marker);
  803. if (i >= 0) {
  804. this._activeMarkers.splice(i, 1);
  805. }
  806. i = this.markersPendingRemoval.indexOf(marker);
  807. if (i >= 0) {
  808. this.markersPendingRemoval.splice(i, 1);
  809. }
  810. this.dispatchEvent(new FLARMarkerEvent(FLARMarkerEvent.MARKER_REMOVED, marker));
  811. marker.dispose();
  812. }
  813. private function onProxyMarkerAdded (evt:FLARMarkerEvent) :void {
  814. this.dispatchEvent(evt);
  815. }
  816. private function onProxyMarkerUpdated (evt:FLARMarkerEvent) :void {
  817. this.dispatchEvent(evt);
  818. }
  819. private function onProxyMarkerRemoved (evt:FLARMarkerEvent) :void {
  820. this.dispatchEvent(evt);
  821. }
  822. //-----<END MARKER DETECTION>---------------------------//
  823. //-----<INITIALIZATION>----------------------------//
  824. private function initConfigLoader () :void {
  825. this.configLoader = new FLARManagerConfigLoader();
  826. this.configLoader.addEventListener(ErrorEvent.ERROR, this.onConfigLoadError);
  827. this.configLoader.addEventListener(FLARManagerConfigLoader.CONFIG_FILE_LOADED, this.onConfigLoaded);
  828. this.configLoader.addEventListener(FLARManagerConfigLoader.CONFIG_FILE_PARSED, this.onConfigParsed);
  829. }
  830. private function disposeConfigLoader () :void {
  831. if (!this.configLoader) { return; }
  832. this.configLoader.removeEventListener(ErrorEvent.ERROR, this.onConfigLoadError);
  833. this.configLoader.removeEventListener(FLARManagerConfigLoader.CONFIG_FILE_LOADED, this.onConfigLoaded);
  834. this.configLoader.removeEventListener(FLARManagerConfigLoader.CONFIG_FILE_PARSED, this.onConfigParsed);
  835. this.configLoader.dispose();
  836. this.configLoader = null;
  837. }
  838. private function onConfigLoaded (evt:Event) :void {
  839. if (this.bVerbose) {
  840. trace("[EZFLAR::FM] config file loaded.");
  841. }
  842. }
  843. private function onConfigLoadError (evt:ErrorEvent) :void {
  844. this.dispatchEvent(evt);
  845. }
  846. private function onConfigParsed (evt:Event) :void {
  847. if (this.bVerbose) {
  848. trace("[EZFLAR::FM] config file parsed.");
  849. }
  850. this.configLoader.harvestConfig(this);
  851. this.init();
  852. this.dispatchEvent(new Event(Event.COMPLETE));
  853. }
  854. private function init () :void {
  855. this.initFlarSource();
  856. this.loadCameraParams(this.configLoader.cameraParamsPath);
  857. this.allPatterns = this.configLoader.patterns;
  858. this.loadPatterns(this.allPatterns);
  859. // initialize sampleBlurFilter
  860. this.sampleBlurring = this.sampleBlurring;
  861. }
  862. private function initFlarSource () :void {
  863. var sourceParent:DisplayObjectContainer;
  864. var sourceAsSprite:Sprite;
  865. var sourceIndex:int;
  866. if (this._flarSource) {
  867. if (this._flarSource.inited) {
  868. // do not attempt to init if source was inited before passing into FLARManager ctor.
  869. return;
  870. }
  871. sourceAsSprite = this._flarSource as Sprite;
  872. sourceParent = sourceAsSprite.parent;
  873. }
  874. if (this.configLoader.useProxy) {
  875. if (sourceParent) {
  876. // if placeholder IFLARSource was already added to the display list, remove it...
  877. sourceIndex = sourceParent.getChildIndex(sourceAsSprite);
  878. sourceParent.removeChild(sourceAsSprite);
  879. }
  880. this._flarSource = new FLARProxy(this.configLoader.displayWidth, this.configLoader.displayHeight);
  881. if (sourceParent) {
  882. // ...and replace it with the new FLARLoaderSource.
  883. sourceParent.addChildAt(Sprite(this._flarSource), sourceIndex);
  884. }
  885. } else if (this.configLoader.loaderPath) {
  886. if (sourceParent) {
  887. // if placeholder IFLARSource was already added to the display list, remove it...
  888. sourceIndex = sourceParent.getChildIndex(sourceAsSprite);
  889. sourceParent.removeChild(sourceAsSprite);
  890. }
  891. this._flarSource = new FLARLoaderSource(
  892. this.configLoader.loaderPath, this.configLoader.sourceWidth,
  893. this.configLoader.sourceHeight, this.configLoader.downsampleRatio);
  894. if (sourceParent) {
  895. // ...and replace it with the new FLARLoaderSource.
  896. sourceParent.addChildAt(Sprite(this._flarSource), sourceIndex);
  897. }
  898. } else {
  899. FLARCameraSource(this._flarSource).addEventListener(ErrorEvent.ERROR, this.onCameraSourceError);
  900. FLARCameraSource(this._flarSource).init(
  901. this.configLoader.sourceWidth, this.configLoader.sourceHeight,
  902. this.configLoader.framerate, this._mirrorDisplay,
  903. this.configLoader.displayWidth, this.configLoader.displayHeight,
  904. this.configLoader.downsampleRatio);
  905. }
  906. }
  907. private function onCameraSourceError (evt:ErrorEvent) :void {
  908. this.deactivate();
  909. this.dispatchEvent(evt);
  910. }
  911. private function loadCameraParams (cameraParamsPath:String) :void {
  912. var loader:URLLoader = new URLLoader();
  913. loader.dataFormat = URLLoaderDataFormat.BINARY;
  914. loader.addEventListener(IOErrorEvent.IO_ERROR, this.onCameraParamsLoadError);
  915. loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onCameraParamsLoadError);
  916. loader.addEventListener(Event.COMPLETE, this.onCameraParamsLoaded);
  917. loader.load(new URLRequest(cameraParamsPath));
  918. }
  919. private function onCameraParamsLoadError (evt:Event) :void {
  920. var errorText:String = "Camera params load error.";
  921. if (evt is IOErrorEvent) {
  922. errorText = IOErrorEvent(evt).text;
  923. } else if (evt is SecurityErrorEvent) {
  924. errorText = SecurityErrorEvent(evt).text;
  925. }
  926. this.onCameraParamsLoaded(evt, new Error(errorText));
  927. }
  928. private function onCameraParamsLoaded (evt:Event, error:Error=null) :void {
  929. var loader:URLLoader = evt.target as URLLoader;
  930. loader.removeEventListener(IOErrorEvent.IO_ERROR, this.onCameraParamsLoadError);
  931. loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onCameraParamsLoadError);
  932. loader.removeEventListener(Event.COMPLETE, this.onCameraParamsLoaded);
  933. if (error) { throw error; }
  934. this._cameraParams = new FLARParam();
  935. this._cameraParams.loadARParam(ByteArray(loader.data));
  936. var sourceSize:Rectangle = this.flarSource.sourceSize;
  937. this._cameraParams.changeScreenSize(sourceSize.width, sourceSize.height);
  938. this.bCameraParamsLoaded = true;
  939. this.checkForInitComplete();
  940. }
  941. private function loadPatterns (patterns:Vector.<FLARPattern>) :void {
  942. this.patternLoader = new FLARPatternLoader();
  943. this.patternLoader.addEventListener(Event.INIT, this.onPatternsLoaded);
  944. this.patternLoader.loadPatterns(patterns);
  945. }
  946. private function onPatternsLoaded (evt:Event) :void {
  947. this.patternLoader.removeEventListener(Event.INIT, this.onPatternsLoaded);
  948. this.bPatternsLoaded = true;
  949. this.checkForInitComplete();
  950. }
  951. private function checkForInitComplete () :void {
  952. if (!this.bCameraParamsLoaded || !this.bPatternsLoaded || !this._flarSource) { return; }
  953. if (this.patternLoader.loadedPatterns.length == 0) {
  954. throw new Error("no markers successfully loaded.");
  955. }
  956. try {
  957. //this.flarRaster = new FLARRgbRaster_BitmapData(this.flarSource.sourceSize.width, this.flarSource.sourceSize.height);
  958. this.flarRaster = new FLARRgbRaster_BitmapData(this.thresholdSourceBitmap.bitmapData);
  959. this.flarSource.source = BitmapData(this.flarRaster.getBuffer());
  960. } catch (e:Error) {
  961. // this.flarSource not yet fully initialized
  962. this.flarRaster = null;
  963. }
  964. this.markerDetector = new FLARMultiMarkerDetector(this._cameraParams, this.patternLoader.loadedPatterns, this.patternLoader.unscaledMarkerWidths, this.patternLoader.loadedPatterns.length);
  965. //this.markerDetector.setContinueMode(true);
  966. if (this.thresholdSourceDisplay) {
  967. // if this.thresholdSourceDisplay was set to true before initialization of
  968. // this.flarSource and this.markerDetector, reset it.
  969. this.thresholdSourceDisplay = true;
  970. }
  971. if (!this.smoother) {
  972. this.smoother = new FLARMatrixSmoother_Average();
  973. }
  974. if (!this.thresholdAdapter) {
  975. this.thresholdAdapter = new DrunkHistogramThresholdAdapter();
  976. }
  977. this.bInited = true;
  978. this.activate();
  979. this.dispatchEvent(new Event(Event.INIT));
  980. }
  981. private function initInversionShader () :void {
  982. var inversionShaderLoader:URLLoader = new URLLoader();
  983. inversionShaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
  984. inversionShaderLoader.addEventListener(Event.COMPLETE, this.onInversionShaderLoaded);
  985. inversionShaderLoader.addEventListener(ErrorEvent.ERROR, this.onInversionShaderLoadError);
  986. inversionShaderLoader.load(new URLRequest("../resources/flar/invert.pbj"));
  987. }
  988. private function onInversionShaderLoadError (evt:ErrorEvent) :void {
  989. this.onInversionShaderLoaded(evt);
  990. }
  991. private function onInversionShaderLoaded (evt:Event, errorEvent:ErrorEvent=null) :void {
  992. var inversionShaderLoader:URLLoader = URLLoader(evt.target);
  993. if (!inversionShaderLoader) { return; }
  994. inversionShaderLoader.removeEventListener(Event.COMPLETE, this.onInversionShaderLoaded);
  995. inversionShaderLoader.removeEventListener(ErrorEvent.ERROR, this.onInversionShaderLoadError);
  996. if (errorEvent) {
  997. throw new Error("invert.pbj not found in ../resources/flar/.");
  998. }
  999. var inversionShader:Shader = new Shader(inversionShaderLoader.data);
  1000. this.inversionShaderFilter = new ShaderFilter(inversionShader);
  1001. this.inverted = true;
  1002. }
  1003. //-----<END INITIALIZATION>---------------------------//
  1004. }
  1005. }