joeltong.org
5Mar/100

Goodbye and A level results!

Hey!

Praise God! Results were very good. At least one less thing to worry about. He saw me through A levels. (:

Tomorrow I am going to serve the country in yet another way - ARMY! (: Will be enlisting soon. Looking forward to it! (:

Something I would like to share today:

8 He has showed you, O man, what is good.
And what does the LORD require of you?
To act justly and to love mercy
and to walk humbly with your God.
- Micah 6:8

Cya in 2 weeks' time! =D

4Mar/100

Memories

2 More days to army. Here's some photos from the Teaching Internship Programme at Nanyang JC. Cheers! :)

PS All the best for results tomorrow!

Get Adobe Flash player

Tagged as: No Comments
27Feb/100

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/

22Feb/100

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

22Feb/100

Random Filter Stuff

Been playing around with some filters in Flash.

Here's a few:

Laplacian of Gaussians i.e. difference of Gaussians - subtract 2 images of varying gaussian blurs to produce image.

Color exchange - flipping the colors using ColorMatrixFilter class.

Enjoy!

PS Just got my new iPhone, I am really in need of an SDK for Windows.  XCode anyone?  Help! =P

16Feb/100

Improved Image Viewer

Get Adobe Flash player

Happy Chinese New Year!! Been dabbling around with my image viewer class during my free time in the past few days. Still purely AS3, albeit with more stuff like:

  • Navigational bar
  • Caption support
  • Page left-page right flipping motion
  • Varied animations to choose from.  If you can't decide, leave it blank and it will randomize it for you.
  • Quite dynamic, and (compiled in CS3) it's only 15kB! xD
  • Auto screen-fit resizing

Screenshot as above! =D   Enjoy!

So, proprietary or open-source? =D

13Feb/100

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);                 }         } }
9Feb/100

Crossdomain.xml woes

Hey,

After much trying, it seems that there's not much that can be done implementing an online radio station player in Flash due to the security sandbox. Realised that crossdomain.xml has to be implemented on the radio station server's side, not mine. AKA, the best would be to run it in adobe Air or within the Flash IDE itself. =(

On a lighter note, I just passed my BTT. Meaning that I can commence my driving lessons. =D

Shall go read up on group theory for machine vision.

Rgds,
Joel Tong

5Feb/100

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

4Feb/100

A pleasant surprise

Hey,

Was surfing the net today, stumbled upon this by coincidence:

Yay. More money to spend on reading =D

Anyways, I am still working on getting the input image for FLaser right with filters. Playing around with Gaussian blur. Seems to improve quite a bit. But there ae quite a few problems remaining.

So here's some snapshots:

Before gaussian


After Gaussian. Notice I can now find the laser "dot" using a blob detection function, and eliminate based on size from there.

Anyways on a sidenote, the laplacian of the gaussian -- basically taking 2 identical images with different gaussian blurs applied to each. Then you bitwise difference them. A thresholded picture of my desk and a packet drink:

Cheers,
Joel Tong

Filed under: Uncategorized No Comments