"...en este mundo traidor nada es verdad ni es mentira, todo es según el color del cristal con que se mira..." --Pedro Calderón de la Barca
Translates approximately to: "...nothing in this treacherous world is true, nor false, all depends on the color of the glass through which we glimpse..."

The Fish Eye Projection

To give my students an idea of a nonlinear projection I use Mathematica 6 to place a fish-eye "lens" in the middle of a picture.

We first clear a few symbols to avoid conflicts in case they have been predefined in a previous run.

"fisheye_1.gif"

We will need to compute Euclidean distances and inverse tangents, being both costly, so we compile them at a small sacrifice in precision to gain some advantage in speed.

"fisheye_2.gif"

"fisheye_3.gif"

The first step is projecting the 3D scene points to a plane perpendicular to the z-axis. This is almost what we get with an ordinary picture, even though they may not really be orthographic projections (unless you use a telecentric lens).

We will use this picture I took in San Francisco during the Spring 2007.

"fisheye_4.gif"

The structure of this image has 3 main parts:

"fisheye_5.gif"

"fisheye_6.gif"

A bit more detailed look at the structure shows that the pixels (raster data) are in the first part, and since this is a color picture it is encoded in the RGB system. This means that the pixel array is the first sublist of the first list.

"fisheye_7.gif"

"fisheye_8.gif"

The pixels are two levels deep into this structure.

"fisheye_9.gif"

"fisheye_10.gif"

We have a way to access each plane, in this case the Red plane.

"fisheye_11.gif"

"fisheye_12.gif"

The RGB values of an individual pixel can be accessed in this way.

"fisheye_13.gif"

"fisheye_14.gif"

Notice that the values are normalized on the [0,1] interval instead of being given as integers in the [0,255] interval, but we don't need to touch them.

We erase variables that could be defined in a previous run of the notebook and cause problems.

"fisheye_15.gif"

Let's define a fish-eye lens with a diameter of 180 pixels, looking at the image's center.

"fisheye_16.gif"

Imagine the picture laying on the x y plane, and the center of projection C is situated at k units on the z-axis in the positive direction. The fish-eye projection brings a point P to another point P', colinear to P and the origin O. The angle P C O is called θ; then, the angle P'C O is "fisheye_17.gif".

"fisheye_18.gif"

In this application it makes sense to do inverse mapping; i.e., knowing the coordinates of the pixels P'inside the circle we find the coordinates of the corresponding point P. This is a fortunate decision because discrete circles are very symmetric objects and we can save memory and computational work because essentially each of the distances O P' is repeated 8 times, so we compute them only for an octant.

"fisheye_19.gif"

The corresponding values of θ are:

"fisheye_20.gif"

"fisheye_21.gif"

The magnitudes of the segments O P are the following:

"fisheye_22.gif"

We change at least one uncomfortable zero, and divide by "fisheye_23.gif" to obtain the ratios "fisheye_24.gif".

"fisheye_25.gif"

"fisheye_26.gif"

We form a full sized array of factors from the octant.

"fisheye_27.gif"

The ratios corresponding to points within the lens are found building a logical array, and using Mathematica's inverse search function "Position[]" to extract them from the full sized array.

"fisheye_28.gif"

To drive processing of pixels from the center to the periphery (to avoid remapping points at a later stage), we order the points according to their sum. This would give us points in progressively bigger balls (although balls in the Minkowski metric, i.e., rhombuses.) This trick will spare us the need to define auxiliary arrays because we can do the pixel substitution in situ. This will save us much memory and time.

"fisheye_29.gif"

"fisheye_30.gif"

"fisheye_31.gif"

"fisheye_32.gif"

"fisheye_33.gif"

"fisheye_34.gif"

We must translate the coordinates in the logical circle array to coordinates in a Cartesian frame.

"fisheye_35.gif"

Now we apply the factors to these coordinates, and translate the origin to the center of the image to obtain the corresponding points. Since the resulting points fall off the lattice we approximate the matching point with its nearest neighbor.

"fisheye_36.gif"

We need to express the coordinates of the points within the lens in the same coordinate system.

"fisheye_37.gif"

Since many points are expected to land outside the picture, we simply disregard them and keep those which fall within.

"fisheye_38.gif"

"fisheye_39.gif"

"fisheye_40.gif"

"fisheye_41.gif"

Similarly, we select those points within the lens which have a feasible match.

"fisheye_42.gif"

"fisheye_43.gif"

"fisheye_44.gif"

We transfer the pixel values to pixels within the lens.

"fisheye_45.gif"

And we display the image.

"fisheye_46.gif"

"fisheye_47.gif"


Created by Wolfram Mathematica 6.0  (06 August 2007) Valid XHTML 1.1!