The Mathematica Journal
Departments
Feature Articles
Columns
New Products
New Publications
Classifieds
Calendar
News Bulletins
Mailbox
Letters
Write Us
About the Journal
Staff and Contributors
Submissions
Subscriptions
Advertising
Back Issues
Home
Download this Issue

Continuous Function Image Manipulation

Convolution is a very powerful image manipulation tool, but it is possible to go much further with Mathematica. We are now going to see how to use the unique combination of symbolic and numerical capabilities of Mathematica to manipulate images in ways impossible in any other system.

The key idea is to transform the image from a discrete matrix of values into a continuous mathematical function, which can then be manipulated mathematically.

Mathematica includes a tremendously clever and powerful feature for doing this. InterpolatingFunction objects, introduced originally to represent the solutions to differential equations, are objects that act just like ordinary functions, but are based on tables of values. The function ListInterpolation takes a list or matrix of numbers, and returns an InterpolatingFunction that can be used to get interpolated values out of the array.

[Graphics:../Images/index_gr_48.gif]
[Graphics:../Images/index_gr_49.gif]

(A small annoyance: The Transpose function, which interchanges rows with columns in the matrix, is necessary because, without it, the x and y dimensions of the resulting function would be interchanged, which is inconvenient.)

Let's see how this function works. Say you want to find out the value of a pixel in the image. You could pick out a particular element from the original data using [[ ]] notation. For example, here is the pixel from the 130th row, 40th column.

[Graphics:../Images/index_gr_50.gif]
[Graphics:../Images/index_gr_51.gif]

Using the InterpolatingFunction, we can get the same value by giving the "function" these same two values as arguments.

[Graphics:../Images/index_gr_52.gif]
[Graphics:../Images/index_gr_53.gif]

So far, nothing remarkable. But the powerful thing about InterpolatingFunctions is that they work even when the arguments are not integers. Using [[ ]] to extract pixels, you can only pick existing pixels. With InterpolatingFunction, you can also pick pseudo pixel values from anywhere in between. For example:

[Graphics:../Images/index_gr_54.gif]
[Graphics:../Images/index_gr_55.gif]
[Graphics:../Images/index_gr_56.gif]
[Graphics:../Images/index_gr_57.gif]
[Graphics:../Images/index_gr_58.gif]
[Graphics:../Images/index_gr_59.gif]

Instead of thinking in terms of arrays of pixels, we can now think in terms of a mathematical function (of two variables) that happens to have z-values that correspond to the brightness of patches of our image. So, let's start thinking mathematically. What's the first thing you do with a function of two variables? Why, plot it of course.

[Graphics:../Images/index_gr_60.gif]

[Graphics:../Images/index_gr_61.gif]

Note that the x and y plot ranges correspond to the number of pixels in the original image. This is merely the default used by ListInterpolate. Later we'll see how to rescale the image to more generic ranges of values. For now, we are still recovering from the disappointment caused by this plot. It certainly doesn't look much like a puppy. But, it turns out this is mainly a matter of the viewpoint. Let's try a slightly different one.

[Graphics:../Images/index_gr_62.gif]

[Graphics:../Images/index_gr_63.gif]

Ah, there's the puppy.

What happens if we apply a function to the x and y variables before passing them to puppyFunction? This has the effect of changing the spacing at which the original image is sampled, as a function of the x and y coordinates. Perhaps an example will make this clear.

[Graphics:../Images/index_gr_64.gif]

[Graphics:../Images/index_gr_65.gif]

This diagram may help explain the distortion better.

[Graphics:../Images/index_gr_66.gif]

The parabolas represents the [Graphics:../Images/index_gr_67.gif] and [Graphics:../Images/index_gr_68.gif] functions we are using to distort the image. Towards the bottom of the image where the slope is shallow, a small portion of the original image is stretched to fill a large portion of the output image. Near the top where the slope is steep, the image is instead compressed. The same thing holds in the left/right direction.

For example, look at the square at the lower right of the original image. Follow the lines, and you'll see that it turns into a much bigger square in the output image. Conversely, the upper left square, which starts out the same size, turns into a smaller square in the output image. The lower left square turns into a wide rectangle, while the upper right square turns into a tall rectangle.

Before we continue, let's redo the puppyFunction so that its x and y values run over more mathematically sensible ranges (namely, [Graphics:../Images/index_gr_69.gif] to 1 in the horizontal direction, and the appropriate range in the y direction to keep the coordinate system square).

[Graphics:../Images/index_gr_70.gif]
[Graphics:../Images/index_gr_71.gif]

[Graphics:../Images/index_gr_72.gif]

[Graphics:../Images/index_gr_73.gif]

[Graphics:../Images/index_gr_74.gif]


Converted by Mathematica      April 21, 2000

[Article Index] [Prev Page][Next Page]