Filters in MathMap 1.3 and above ================================ MathMap 1.3 features a new addition to the language: The ability of filters to call other filters, and to apply filters to an image to create a new "virtual" image which can in turn be passed to other filters. This text is an introduction to this new feature. Calling filters for pixels -------------------------- Let's define a filter which flips an image horizontally: filter flip_x (image in) in(xy:[-x,y]) end We might now want to define a filter which blends an image with its mirror image. Previously, we would have to do something like this: filter blend_flip_x (image in, float alpha: 0 - 1 (0.5)) pixel = in(xy); flipped_pixel = in(xy:[-x,y]); pixel * alpha + flipped_pixel * (1 - alpha) end In other words, it was not possible to abstract the flipping operation. It had to be inlined manually instead. With MathMap 1.3 we can express the same filter using flip_x as a building block like this: filter blend_flip_x (image in, float alpha: 0 - 1 (0.5)) pixel = in(xy); flipped_pixel = flip_x(in, xy); pixel * alpha + flipped_pixel * (1 - alpha) end As you can see we can call the filter "flip_x" by giving it the arguments it expects (in this case a single input image) plus the coordinates of the pixel we are asking it to compute. Applying filters to images -------------------------- We can even go one step further and also abstract the blending operation, by first defining a blending filter: filter blend (image i1, image i2, float alpha: 0 - 1) p1 = i1(xy); p2 = i2(xy); p1 * alpha + p2 * (1 - alpha) end Now we can apply the "flip_x" filter to the whole input image to create a flipped image which we can then pass to "blend": filter blend_flip_x (image in, float alpha: 0 - 1 (0.5)) blend(in, flip_x(in), alpha, xy) end Note that we do not pass the coordinates to "flip_x" this time, because we don't just want a color - we want a whole image instead. We do, on the other hand, have to pass xy to "blend", because our filter is expected to return a color. When you declare multiple filters in a MathMap script then the last one is the one that is actually run. A filter can only use filters which are declared above it, including itself, i.e. filters can be recursive. That's everything as far as syntax and basic functionality goes. What follows is a discussion of the finer points. Applying filters doesn't really generate images ----------------------------------------------- I've said above that applying "flip_x" to an image creates a new image containing a flipped copy of the original image. This is not true in the sense that a new bitmap is generated for this image. The new image is instead "virtual". A virtual image is not represented by a bitmap but instead by a piece of code (the filter) and some data (the arguments to the filter) that specify exactly what to do in order to get the color value for some point of the image. Such a virtual image has two properties which bitmap images do not have: It extends infinitely in both dimensions, i.e. it is infinitely large, and it can have arbitrarily small levels of detail. As a simple illustration of these two properties let's play with these two filters: filter zoom_in (image in) in(xy / 2) end filter zoom_out (image in) in(xy * 2) end The filter "zoom_in" enlarges the image by a factor of 2, while "zoom_out" scales the image down. If you were to apply first "zoom_in" to an image, and then "zoom_out" to the result, you would not get the image you started with, but instead only the middle part of it with black borders around, because applying "zoom_in" discards those parts. If you applied the filters the other way around you would get the whole image, but its quality would be reduced, because scaling down an image inevitably destroys information. Let's try to combine the filters in MathMap now: filter ident (image in) zoom_out(zoom_in(in), xy) end This corresponds to our first case: Zooming in, then zooming out. Because the virtual image "zoom_in(in)" is not limited in size the borders do not get lost and "zoom_out" recovers everything. This is the second case: filter ident (image in) zoom_in(zoom_out(in), xy) end We do not lose information here, because virtual images have unlimited resolution. Nit-picking note: While virtual images do have very high resolution and extend very far, their resolution and size is actually limited by floating point precision. In practice this should never be an issue, though. Colors ------ Another nice property of virtual images is that they also have a much larger range of colors than ordinary images. Color values are not clamped for virtual images, so they can extend beyond the range of 0 to 1. Performance ----------- The MathMap compiler implements filters and virtual images in such a way that their overhead should in most cases be zero, i.e. there should be no difference in performance between using a filter and doing the inlining manually. That's because MathMap does in fact always inline calls to filters and virtual images, with the exception of recursive filters. -- Mark Probst