FLaser on Youtube
Hi there,
Just posted 2 videos from my FLaser Ver 1.0 (2 years ago) on Youtube. You can view it here:
An excerpt:
Preamble:
This project proposes a cost-effective solution of interacting with a projector screen using a laser pointer, via an ordinary webcam.
The system was deployed and scripted in Flash Actionscript 3.0, back in Dec 2007 to Jan 2008.
It subsequently won the Most Innovative Award and Best Exhibit at the Singapore Shell Science Fair 2008, was shortlisted by MOE to represent Singapore in the Taiwan International Science Fair (TISF) 2008, in addition to being an exhibited finalist at the A*STAR Singapore Science and Engineering Fair (SSEF) 2009.
For more information, please checkout http://blog.joeltong.org/ .
Method:
The system comprises of a screen image from a projector. A webcam is positioned to track the projector screen area.
The steps for successful detection are as follows:
1. Calibration - 4 Red dots are shown on-screen. Thresholding red, the system is able to pick out the distance between the four red dots, demarking the tracked active area by the webcam. A ratio is then computed which is used to position the mouse.
2. A red laser dot is then shone onscreen. Minor adjustment knobs compensate for the offset in terms of the computed position of the laser dot and its actual position, similar to zeroing in using a crosshair.
3. The system is ready to track the laser pointer. In this case, a much high threshold is used, from the fact that red laser beams have a much higher intensity from the surrounding background.
System has been shown to detect laser dot despite showing red noise. However, alternative algorithms are being devised fo9r the system for better background noise tolerance.
Do checkout the version shown at Shell Science Fair @ LINK. System is targeted at rhe Flash platform, and for home users.
For more enquiries,
Email: me {at} joeltong {dot} org.
Thanks!
Best regards,
Joel Tong
http://blog.joeltong.org/
XML Powered Gallery / Display Header (TOTALLY IN AS3)
Hey,
Currently I am at work doing a custom utils library for Flash. Of course, totally (or mostly) written in AS3 xDD. Just came up with the basic skeleton of an XML-powered header. Something like what I did for E Club, except this time it will be much more customizable and easier to use for people w/o Flash. Elegant - 12kB and I hope to put it more features too! =D
See this site for a live demonstration.
Since source is rather short, I shall post it here as well. Look forward to a ver. 2 in the future! =)
package org.joelTong.utils.displayImages { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.Sprite; import flash.net.*; import flash.events.*; import flash.utils.Timer; import caurina.transitions.*; /** * joeltong.org * @author JOELTONG * joel [dot] tong [at] gmail [dot] com */ public class ImageBanner extends Sprite { private var WIDTH :uint; private var HEIGHT :uint; private var _images :Array; //this is the image that is shown on stage private var _imageList :Array; //this is the loader for the images i.e. precached private var _xmlList :XML; //this is the xml file private var _timer :Timer; //switching purposes private var _topIndex :uint; //the index of the topmost image currently (1 or 0). private var _curPic :uint; //the current index in imageList (up to the size of array). public function ImageBanner(w:uint = 0, h:uint = 0 ):void { super(); var loader:URLLoader = new URLLoader(); _images = new Array(); _imageList = new Array(); _timer = new Timer(4000, 0); WIDTH = w; HEIGHT = h; _images = [new Bitmap(new BitmapData(WIDTH, HEIGHT, true, 0xFFFF0000)), new Bitmap(new BitmapData(WIDTH, HEIGHT, true, 0xFF00FF00))]; loader.dataFormat = URLLoaderDataFormat.TEXT; loader.addEventListener(Event.COMPLETE, onXmlLoaded); loader.load(new URLRequest("bannerList.xml")); //add to container addChild(_images[1]); addChild(_images[0]); //set up boundable area var mask:Sprite = new Sprite(); mask.graphics.beginFill(0xFF0000); mask.graphics.drawRect(0, 0, WIDTH, HEIGHT); this.mask = mask; this.addEventListener(MouseEvent.CLICK, onClick); } private function onXmlLoaded(e:Event):void { _xmlList = new XML(e.target.data); trace(_xmlList.entry.length()); precacheImages(); } private function precacheImages():void { for (var i:uint = 0; i < _xmlList.entry.length(); i++) { _imageList.push(new Image(WIDTH,HEIGHT)); _imageList[i].setLoading(_xmlList.entry[i].location); } this.addEventListener(Event.ENTER_FRAME, preloadImgs); } private function preloadImgs(e:Event):void { if (checkLoading()) { removeEventListener(Event.ENTER_FRAME, preloadImgs); setupTimer(); } } private function checkLoading():Boolean { for (var i:uint = 0; i < _xmlList.entry.length(); i++) { if (!_imageList[i].isLoaded()) { return false; } } return true; } private function setupTimer():void { _timer.addEventListener(TimerEvent.TIMER, onTimer); _timer.start(); _topIndex = 0; _curPic = 1; _images[0].bitmapData.draw(_imageList[0]); _images[1].bitmapData.draw(_imageList[1]); } private function onTimer(e:TimerEvent = null):void { Tweener.addTween(_images[_topIndex], { x: WIDTH, time: 1, transition:"easeInElastic", onComplete: swap } ); } private function swap():void { trace("called"); this.swapChildren(_images[0], _images[1]); _topIndex = (_topIndex + 1) % 2; _curPic = (_curPic + 1) % _xmlList.entry.length(); trace("CURPIC: " + _curPic); trace("INDEX: " + _topIndex); trace(_imageList.length); //TODO: FIX THIS BUG _images[(_topIndex+1) % 2].bitmapData.draw(_imageList[_curPic]); _images[(_topIndex+1) % 2].x = 0; _images[(_topIndex+1) % 2].y = 0; } private function onClick(e:MouseEvent = null):void { navigateToURL(new URLRequest(_xmlList.entry[_curPic].url), "_blank"); } /* private function onComplete(e:Event):void { var bit:Bitmap = new Bitmap(new BitmapData(500, 200, true, 0xFFFF0000)); bit.bitmapData.draw(e.target.content); addChild(bit); }*/ } }
package org.joelTong.utils.displayImages { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.events.Event; import flash.net.URLRequest; /** * joeltong.org * @author JOELTONG * joel [dot] tong [at] gmail [dot] com */ public class Image extends Bitmap { private var WIDTH :uint; private var HEIGHT :uint; private var loader :Loader; private var state :String; public function Image(w:uint = 100, h:uint = 100):void { WIDTH = w; HEIGHT = h; super(new BitmapData(WIDTH, HEIGHT, true, 0xFFFF0000)); state = "LOADING"; } public function setLoading(url:String):void { loader = new Loader(); loader.load(new URLRequest(url)); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImgLoaded); } private function onImgLoaded(e:Event):void { this.bitmapData.draw(e.target.content); state = "COMPLETE"; } public function isLoaded():Boolean { if (state == "COMPLETE") { return true; } else { return false; } } //getters and setters--------------------------------- } }
package { import flash.display.Sprite; import org.joelTong.utils.displayImages.ImageBanner; /** * joeltong.org * @author JOELTONG * joel [dot] tong [at] gmail [dot] com */ public class Main extends Sprite { private var imageBanner:ImageBanner = new ImageBanner(1024,768); public function Main():void { addChild(imageBanner); } } }