In addition to functions which perform specific geometric transforms (MimFlip(), MimResize(), MimRotate(), MimTranslate(), and MimPolarTransform()), MIL includes a more general geometric function, MimWarp(). It can perform any of the specific transforms, as well as complex warpings. Specifically, it can perform: first-order polynomial warpings, perspective polynomial warpings, polar-to-rectangular (and vice versa) warpings, and custom warpings.
Note that the functions which perform specific transforms are faster than MimWarp(). You should only use MimWarp() when the required transform cannot be otherwise performed. In addition, geometric distortions can also be resolved using the Camera Calibration module. For more information, see Chapter 26: Calibrating your camera setup.
MimWarp() performs a warping by first associating each pixel position of the destination buffer, (x_{d}, y_{d}), with a specific point (not necessarily a pixel) in the source buffer, (x_{s}, y_{s}). The pixel value at (x_{d}, y_{d}) is then determined from an interpolation around its associated source point. Destination pixels can be associated with source points in two different ways:
Using a 3x3 coefficients matrix that is used as follows:
Using LUTs (an X-LUT and a Y-LUT) that are the same size as the destination image and are used as follows:
X _{s} = LUT_{X}[X _{d}, Y _{d}].
Y _{s} = LUT_{Y}[X _{d}, Y _{d}].
If you intend on performing the same warping operation on multiple images (which would require using the same LUTs), it might be faster to generate the LUTs for the operation once and repeatedly pass them to MimWarp(). However, if different warpings are required, or only one image is to be processed, it is faster to generate the coefficients and call MimWarp() than it is to generate the LUTs and then call MimWarp().
A first-order polynomial warping is equivalent to a combination of a linear translatation, rotation, resizing, and/or shearing of an image. First-order polynomial warpings are performed by associating points in the source buffer with pixels in the destination buffer according to the following equations:
X _{s} = a_{0} X _{d} + a_{1} Y _{d} + a_{2} .
Y _{s} = b_{0} X _{d} + b_{1} Y _{d} + b_{2} .
To perform a first-order polynomial warping using a 3x3 coefficients matrix, you must pass MimWarp() the identifier of a single-band, 32-bit floating-point, 3x3 M_ARRAY buffer filled with the coefficients to MimWarp(). The elements of the last row of the coefficient matrix should be [0, 0, 1]. You can also specify a 3x2 M_ARRAY buffer filled with the coefficients, and the elements of the last row are assumed to be [0, 0, 1]. The coefficients of this matrix can be:
Generated automatically using MgenWarpParameter().
Generated using the basic transformation functions MimResize(), MimRotate(), and MimTranslate().
User-established.
When using MgenWarpParameter(), specify how to perform the warping (for example, specify by how much to rotate and resize an image); the function then generates the coefficients required to produce such a warping. To combine coefficients, use separate calls to MgenWarpParameter(). For example, to generate coefficients for a rotation and translation, call MgenWarpParameter() twice, using the output buffer of the first call as the input buffer of the second call. To create the LUTs using MgenWarpParameter(), you can either pass the same information as required to generate the coefficients or you can pass the 3x3 coefficients matrix itself.
When using the basic transformation functions to generate coefficients, specify the source image buffer as M_NULL, and provide transformation details. You must also provide a destination buffer that has an M_ARRAY attribute and that has dimensions of 3x3 to store the generated coefficients. For more information, see the individual reference page for each function.
A perspective polynomial warping is used to map an arbitrary quadrilateral onto a rectangle or to map a rectangle onto an arbitrary quadrilateral.
Perspective polynomial warpings are performed by associating points in the source buffer with pixels in the destination buffer according to the following equations:
X _{s} = (a_{0} X _{d} + a_{1} Y _{d} + a_{2})/(c_{0} X _{d} + c_{1} Y _{d} + c_{2}).
Y _{s} = (b_{0} X _{d} + b_{1} Y _{d} + b_{2})/(c_{0} X _{d} + c_{1} Y _{d} + c_{2}).
To perform a perspective polynomial warping using a 3x3 coefficients matrix, you must pass MimWarp() the identifier of a single-band, 32-bit floating-point 3x3 M_ARRAY buffer filled with the coefficients. The coefficients of this matrix can be generated automatically using MgenWarpParameter(), or can be user established. When using MgenWarpParameter(), you must specify the coordinates of the four corners of the quadrilateral or the two opposite corners of the rectangle. The coordinates are illustrated in the image below.
In addition to first-order polynomial and perspective polynomial warpings, it is possible to perform more complex warping operations using MimWarp(), if used with two 2D LUTs. In this case, the LUTs map destination pixels (X_{d}, Y_{d} ) to specified points (X_{s} , Y_{s} ) in the source image buffer as follows: X_{s} is determined from (X_{d}, Y_{d} ) through one LUT (X-LUT) and Y_{s} is determined from (X_{d}, Y_{d} ) through another LUT (Y-LUT). Both LUTs should have the same X and Y-size as the destination image.
You can generate these LUTs using MimPolarTransform(), to warp from the polar to the rectangular coordinate system (or vice versa). Alternatively, you can fill the LUTs with a custom transformation generated from another source. Once the X-LUT and Y-LUT are loaded with your values, you can pass them to MimWarp() to perform the warpings.
A polar-to-rectangular warping takes a polar image as is shown in the left-most image, and transforms it to a rectangular image, as shown in the right-most image. See the Polar-to-rectangular and rectangular-to-polar transforms section earlier in this chapter for more information.
To perform polar-to-rectangular or rectangular-to-polar transformations using MimWarp(), first call MimPolarTransform() with the required information to perform the transformation. However, instead of specifying a source and destination image, specify a 2D LUT buffer (M_LUT) to store the X-coordinate mapping, and a 2D LUT buffer to store the Y-coordinate mapping. Then, call MimWarp() with the LUTs and the source and destination image buffers. The LUT buffers that you specify must be the same size as the destination image.
To perform a custom warping using MimWarp(), you must fill two 2D LUTs (M_LUT) representing the X and Y-coordinates, using MbufPut2d(). Every position in the X-LUT (X, Y) specifies the X-coordinates (X_{s} ) of the point in the source image every position in the Y-LUT (X, Y) specifies the Y-coordinates (Y_{s} ) in the source image. This type of warping arbitrarily maps the pixel position of the destination buffer to a specific point in the source buffer, based on what was passed to the LUTs in MbufPut2d(). You can specify sub-pixel coordinates for the source point.
When you perform a warping, each pixel position in the destination buffer (X_{d}, Y_{d}), gets associated with a specific point in the source buffer (X_{s}, Y_{s}), and a computed intensity value for (X_{s}, Y_{s}) is then copied to (X_{d}, Y_{d}). The destination coordinates have integer values but the source coordinates, in general, do not. Therefore, the pixel value at (X_{d}, Y_{d}) is determined from several source pixels that are near (X_{s}, Y_{s}), according to a specified interpolation mode.
The various interpolation modes available when using MimWarp() are:
A standard nearest neighbor interpolation (M_NEAREST_NEIGHBOR).
A standard bilinear interpolation (M_BILINEAR).
A standard bicubic interpolation (M_BICUBIC).
In general, nearest-neighbor interpolation is the fastest to perform, and bicubic interpolation is the slowest. However, nearest-neighbor interpolation produces the least accurate results, and bicubic interpolation produces the most accurate. Bilinear interpolation is often the best compromise between speed and accuracy.
For more information on interpolation modes, see the Interpolation modes subsection of the Basic geometric transforms section of Chapter 3: Fundamental image processing.
Sometimes, the point associated with a destination pixel will fall outside the source buffer. In such cases, the new value for the destination pixel can be determined in one of the following ways:
You can use pixels from the source buffer's ancestor buffer. If the source buffer is not a child buffer or if the point falls outside the ancestor buffer, the destination pixel will be left as is.
You can just leave the destination pixel as is.
You can set the destination pixel to 0.
In general, you should use pixels from the source buffer's ancestor buffer when the source buffer is a child buffer. This will ensure that the pixels you use are related to the source buffer. If the source buffer is not a child buffer, use one of the other options.
Note that you can set the destination pixels that correspond to values outside the source buffer to a fixed value other than 0 by first clearing the destination buffer to that value.
If you only want to establish the location in the source image to which destination positions are mapped, you can use MimWarpList() to transform a list of coordinates (points) using a specified warping matrix. By default, MimWarpList() transforms the coordinates using the specified warping as is. This is referred to as a reverse warping transformation (M_REVERSE) because this is how the MimWarp() function associates each pixel position of the destination buffer with a specific point in the source buffer.
You can also have MimWarpList() use the inverse of the specified matrix (M_FORWARD); this is referred to as a forward warping transformation. This type of transformation is useful if you need to determine if a specific pixel in the source image is actually mapped to a destination buffer pixel.
You can only pass MimWarpList() a coefficient matrix; this allows for either first-order polynomial warpings or perspective polynomial warpings. Regardless of whether performing an M_REVERSE or an M_FORWARD transformation, pass the points to transform in an array to the SrcCoordXArrayPtr and SrcCoordYArrayPtr parameters. This is because the operation is the same; only the coefficient matrix is affected.
A warping example, MimWarp.cpp, can be found in your MIL examples directory.