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/
Radial Thresholding in AS3 (Webcam)
Perhaps one of the things I would like to do before NS - build a radial threshold filter.
Conventional threshold filters are constant throughout. This is means that if let's say the image threshold value is 170, then that threshold value is applied to the WHOLE image. That is pretty bad for some cameras.
Back when I was working on my DSI project, I suggested implementing a radial threshold. Bah, not enough time then. This time, modular approach, OOP-based, voila.
My aim is to implement a radial threshold with a 2D-based function. In some webcams, the distribution of light may not be even, even though it IS even. It's just picked up with bias. For example, let's say we want the threshold to be a logarithmic function. Then we would use y = ln (x) or something (sometimes this works especially if you want the threshold to level out sooner than later.
All we have to do is then input this expression into the code, then by doing per-pixel iteration, we would be able to map that certain value for the specific pixel with the corresponding threshold value onto the image. Now, in this case we will be plotting 2 graphs: one of the image and another of threshold versus the length of the line from the origin (centre of circle).
See the pic for more info
Hopefully I can make an implementation fast enough to be used for video manipulation in AS3 =D
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); } } }
Reflection
Hey,
I think the screen isn't as reflective as it should be. Hopefully it will work better on a wall? Haha. Anyways, thinking of doing this:
max contrast -> max saturation -> greyscale -> gaussian blur -> threshold -> final img.
Rgds,
Joel Tong
FLaser
Hi,
Been busy the past few days trying to do a Ver. 2.0 of my Science Fair project. So original name from "STUDY OF LUMINOUS INTENSITY AND ITS APPLICATIONS IN LASER-AIDED VIRTUAL ON-SCREEN IMAGE INTERACTION SYSTEMS", changed it to FLaser. (Obviously, FL being Flash). xD
Managed to get clearance from my mentor @ A* STAR DSI to further develop it. You can read more of it here:
- http://temasek.wetpaint.com/page/DSI:+Creation+of+LASER-aided+virtual+on-screen+image+interaction+systems
- http://www.science.edu.sg/ssc/ssef-ats/publicview.jsp
Apparently, IHPC is doing something similar for the Mac called Lightdraw. Will see if I can port / improve some of the algos and source code in their project. Link here:
And something I stumbled upon for augmented reality in Flash (FLAR):
Pretty cool huh? =D
Cheers,
Joel Tong
Update on Today
Hey there,
Well spent some time looking at getid3() . Was trying to make a customized PHP to XSPF converter for Dingle using the getid3() package, but didn't manage to do so after staring 5 hours at the documentation. Haha. Probably would source for another library (if I have the time =P).
Anyways, came up with an auto-gen using PHP. So upload songs to directory, access localhost, and tada. =D
Anyone want to help convert the ID3 tags to XSPF format using PHP? PM me =D
See you @ the ASFUG meeting if you are going! It's this Sat.
PS Something to remember:
9 A man’s heart plans his way,
But the LORD directs his steps.
[Proverbs 16:9]
Dingle MP3 Player Ver. 1.1.0
Hey,
Well Dingle Ver. 1.1.0 has been released. New features:
- Dynamic resizing. Aka, you can resize the MP3 player using HTML (@ runtime) using swfobject, swffit, etc.
- 3 new visualizations. To switch, enter a value (0 to 2) for the var DINGX_VISUALIZATION_TYPE found in Config.as (Your configurations file).
- Menu added.
- Usage and workable example (minus the songs, do your own XML list compilation) found in folder /deploy.
Things to add in future:
- Buffering progress bar
- autogenerated XML file using PHP. Any takers? =P
- in the XML file, hopefully I can rely on the duration entry for sound tracking. Notice that if your sound is not precached, progress bar tends to change in terms of total bytes loaded.
- DingX's 3D visualizations. Still quite underdeveloped.
Download the source here: http://www.box.net/shared/1ke8nkn24c
See the preview here: http://www.joeltong.org/dingle/
PS Hopefully I can get my hands on CS4 / future CS5? The Sound.extract() function seems quite promising. Maybe sound manipulation at the byte level in future as a possible project? =D
Update: A-SFUG conference this Saturday, Ogilvy Centre, 11am to 1pm. See you there if you are going!
Rgds,
Joel Tong
Dingle MP3 Music player
Hey,
Just done with cleaning up the Dingle MP3 music player. Now comes with a comprehensive configuration that allows you to color and set just about anything (including the visualizations, fonts, glow, dimensions, etc.). =P Final file size? 33kb (w/o embedded fonts). If you are gonna embed fonts, 120kb =P
Global variables file can be found in Config.as .
Enjoy! =D
Made a color scheme called fiery:
Dingle Music Player
Heh been building a music player over the past 2 days while I am sick... Introducing DingX's cousin, Dingle! =D
Well okay. Dingle is a simple music player that supports DingX visualizations. Well, no surprise I hope? =P Screenshot as above (quality of screenshot is pretty bad). Probably might go retail (or free?) in a couple of days time. Code needs some tidying up and additional eye candy yeah
Some features of Dingle:
- Totally AS3 powered - no MC / Buttons / bg pics was used. AKA, just compile and run!
- Graphics, animation, etc. completely written in code! Small file size because everything is written in CODE!
- Based on OOP - meaning it is really modular. Also features singleton classes for management.
- Support for the sound spectrum visualizations using my DingX engine
- XML support for songlist - easy updating!
- Auto looping!
I might decide to put in a pause bar. There's basically not much need for a stop button since you are already visiting that site =P
Cheers!


