Under the hood, many of the transformations to follow have a certain common element. In particular, they will be taking pixels from one place in the image and mapping them to another place. In this case, there will always be some smooth mapping, which will do what we need, but it will not always be a one-to-one pixel correspondence.
We sometimes want to accomplish this interpolation programmatically; that is, we'd like to apply some known algorithm
that will determine the mapping. In other cases, however, we'd like to do this mapping
ourselves. Before diving into some methods that will compute (and apply) these mappings for
us, let's take a moment to look at the function responsible for applying the mappings that
these other methods rely upon. The OpenCV function we want is called cvRemap()
:
void cvRemap( const CvArr* src, CvArr* dst, const CvArr* mapx, const CvArr* mapy, int flags = CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, CvScalar fillval = cvScalarAll(0) );
The first two arguments of cvRemap()
are the source
and destination images, respectively. Obviously, these should be of the same size and number
of channels, but they can have any data type. It is important to note that the two may not
be the same image. [73] The next two arguments, mapx
and mapy
, indicate where any particular pixel is to be relocated.
These should be the same size as the source and destination images, but they are
single-channel and usually of data type float
(IPL_DEPTH_32F)
. Noninteger mappings are OK, and cvRemap()
will do the interpolation calculations for you automatically. One
common use of cvRemap()
is to rectify (correct
distortions in) calibrated and stereo images. We will see functions in Chapters Chapter 11 and Chapter 12
that convert calculated camera distortions and alignments into mapx
and mapy
parameters. The next argument
contains flags that tell cvRemap()
exactly how that
interpolation is to be done. Any one of the values listed in Table 6-1 will work.
Table 6-1. cvWarpAffine() additional flags values
flags values |
Meaning |
---|---|
|
Nearest neighbor |
|
Bilinear (default) |
|
Pixel area resampling |
|
Bicubic interpolation |
Interpolation is an important issue here. Pixels in the source image sit on an integer grid; for example, we can refer to a pixel at location (20, 17). When these integer locations are mapped to a new image, there can be gaps—either because the integer source pixel locations are mapped to float locations in the destination image and must be rounded to the nearest integer pixel location or because there are some locations to which no pixels at all are mapped (think about doubling the image size by stretching it; then every other destination pixel would be left blank). These problems are generally referred to as forward projection problems. To deal with such rounding problems and destination gaps, we actually solve the problem backwards: we step through each pixel of the destination image and ask, "Which pixels in the source are needed to fill in this destination pixel?" These source pixels will almost always be on fractional pixel locations so we must interpolate the source pixels to derive the correct value for our destination value. The default method is bilinear interpolation, but you may choose other methods (as shown in Table 6-1).
You may also add (using the OR operator) the flag CV_WARP_FILL_OUTLIERS
, whose effect is to fill pixels in the destination image
that are not the destination of any pixel in the input image with the value indicated by the
final argument fillval
. In this way, if you map all of
your image to a circle in the center then the outside of that circle would automatically be
filled with black (or any other color that you fancy).
[73] A moment's thought will make it clear why the most efficient remapping strategy is incompatible with writing onto the source image. After all, if you move pixel A to location B then, when you get to location B and want to move it to location C, you will find that you've already written over the original value of B with A!