Flashy Fisheye
Friday, April 20th, 2007Today I figured I’d make some fisheye lens out of my webcam. So I did.
It worked out fine, even though it isn’t optimized in any way. Here you have a look at my garden:

Of course, you can also try this for yourself if you like. To spice things up, I’ve got the source here for you:
import flash.display.*;
var my_cam:Camera = Camera.getCamera();
my_cam.setMode(320,240,10);
var my_video:Video = new Video();
my_video.width = my_cam.width;
my_video.height = my_cam.height;
my_video.attachCamera(my_cam);
var bmd_video:BitmapData = new BitmapData(my_cam.width,my_cam.height);
var bm_video:Bitmap = new Bitmap(bmd_video);
bm_video.x = 0;
bm_video.y = 0;
this.addChild(bm_video);
//size of the fish-eye BitmapData
var feSize:uint = Math.min(bmd_video.width, bmd_video.height);
var bmd_fisheye:BitmapData = new BitmapData(feSize,feSize,false,0xFF000000);
var bm_fisheye:Bitmap = new Bitmap(bmd_fisheye);
bm_fisheye.x = my_cam.width;
bm_fisheye.y = 0;
this.addChild(bm_fisheye);
var updateInterval:uint = setInterval(update,100);
function update() {
bmd_video.draw(my_video);
//hw,hh = centre of the video bitmap
var hw:uint = bmd_video.width / 2;
var hh:uint = bmd_video.height / 2;
//radius of the 'lens'
var radius:uint = feSize / 2;
for (var x:uint = 0; x < feSize; x++) {
for (var y:uint = 0; y < feSize; y++) {
//calculate distance to the centre
var dx:int = x - radius;
var dy:int = y - radius;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
//skip the rest if outside of the circle we want to draw
if (dist > radius) continue;
//This code calculates which pixel should be copied from
//the original bitmap.
//first, convert the current distance to a float 0..1,
//indicating zero to maximum distance.
var ndist:Number = dist / radius;
//multiply by PI/2 - which is equivalent to an angle of 0-90°
ndist *= (Math.PI / 2);
//Do a cosine on that, which makes the 'fish-eye' effect come true
ndist = Math.cos(ndist);
//Since this is still a value between 0 and 1, multiply by the radius
ndist *= radius;
//It is still inverse (distance 0 -> cos(0) = 1; distance 1 -> cos(pi/2) =0)
//substract ndist from radius
ndist = radius - ndist
//the angle at which the current pixel is located
var angle = Math.atan2(dx,dy) + Math.PI / 2;
//calculate x and y in original picture,
//x is flipped and they are relative to the centre of the image
var ox:int = Math.cos(angle) * ndist;
var oy:int = Math.sin(angle) * ndist;
//make them relative to the upperleft corner of the image
ox = hw - ox;
oy = hh + oy;
//finally, copy the pixels
bmd_fisheye.setPixel(x,y,bmd_video.getPixel(ox,oy));
}
}
}
Surely things can be done a lot faster with specialized filters, but I just wanted to see if I could work this out myself. Perhaps I’ll improve this later.


