Fisheye effect in ImageMagick

September 5, 2008
A fisheye view of Obama

A fisheye view of Obama

ImageMagick is my favorite tool for messing around with images.  At PhotoBucket, we dedicated dozens of machines to “mogrify” uploaded images into thumbnails and appropriate sizes for user accounts.  I use it on my pet projects like an image zoomer and a youtube clipper.

Recently I discovered the “-fx” or special effects options of the ImageMagick toolset.  This was written in 1996 as probably a neat hack.  The code interprets a calculator-like FX language for each channel of every pixel.   This is painfully slow and screams for optimization.  Yet its so much fun. Take the following snippet of FX:

kk=w*0.5;
ll=h*0.5;
dx=(i-kk);
dy=(j-ll);
aa=atan2(dy,dx);
rr=hypot(dy,dx);
rs=rr*rr/hypot(kk,ll);
px=kk+rs*cos(aa);
py=ll+rs*sin(aa);
p{px,py}

Variable names are tricky. Several characters are reserved to represent pixels (p), various channels (r, g, b), locations (i,j), sizes (w,h) and more.

Lines 1 and 2 of the FX code calculates the center of an image in x,y coordinates as (kk,ll).

Lines 3 through 6 compute the polar coordinates of the current pixel i,j as a radius rr and angle aa. The internal function hypot() is a shortcut for hypotenuse of a triangle, which is the Euclidean distance between points (kk,ll) and the current pixel (i,j).

Now comes the silly part. I take the radius of the polar coordinates and square it, then divide by maximum radius of the image, for a new radius RS for “radius scaled” in Line 7. From there, I calculate the cartesian coordinate that correponds to a pixel at radius RS from the center, at the original angle AA, in lines 8 and 9. This creates a 2d bell-shaped surface that is flat at the edges curves up toward infinity at the center. Finally, Line 10 stores the pixel located at (px,py) in the current pixel (i,j) using the “p” operator.

I store this equation in a file, “effect.fx”. I downloaded a picture of Obama as obama.jpg:

Obama as we all see him

Obama as we all see him

Next I applied convert using the fx operator:

convert -fx @effect.fx obama.jpg fish.jpg

This takes a few minutes on my MacBook pro. The result is a fisheye view of Obama! Fun hack, with apologies to Obama.

Leave a Reply