-
Notifications
You must be signed in to change notification settings - Fork 37
Affine Operation
This new version of the JAI Affine Operation is provided for supporting ROI and No Data values during computation. The various step for achieving this goal are described below.
WORKFLOW
- Creation of the jt-affine module inside the jai-ext project.
- Creation of a JUnit test-class for checking if these new operators have a correct behaviour.
- Modification and extension of the JAI AffineOpImage class adding the ROI and No Data support.
- Creation of a RenderedImageFactory and a Descriptor for registering the new Operator and describing its functionalities.
This new version of the Affine operation uses 7 classes:
- AffineOpImage.java : this class is similar to the old AffineOpImage class but the computeTile() method takes into account the presence of the ROI.
- AffineDescriptor.java : this class describes the parameters used for executing an affine operation on the selected image and provides an efficient method for calling the AffineOpImage on it.
- AffineCRIF.java : this class is a ContextualRenderedImageFactory used by the JAI api when the method JAI.create("AffineNoData") is called.
- AffineNearestOpImage.java : this class extends the AffineOpImage class and is used when the Nearest interpolation is need.
- AffineBilinearOpImage.java : this class extends the AffineOpImage class and is used when the Bilinear interpolation is need.
- AffineBicubicOpImage.java : this class extends the AffineOpImage class and is used when the Bicubic interpolation is need.
- AffineGeneralOpImage.java : this class extends the AffineOpImage class and is used when the Interpolation type is different from the 3 above or the image is binary.
The affine transformation is an operation which maintains the lines straight and parallels. Transformations with these characteristics are: translation, scale, rotation and shear. The affine transformation selected can be passed by using an instance of the AffineTransformation class.
The AffineOpImage.java class is an abstract class used for inizialization and containing some accessor methods.
The 2nd and 3rd classes are used when a new affine operation is requested. When the AffineDescriptor.create() method is executed, the input parameters are packed and used with the JAI.create("AffineNoData") method. Then the JAI api searches for the RenderedImageFactory associate to the operation: AffineCRIF.java. By evaluating the transformation matrix values and the presence of the ROI, the AffineCRIF choose which class should be called for performing the requested operation. For example, if the affine transformation is a simple translation of an integral value without using any ROI object, then the RenderedImageFactory calls the TranslateIntOpImage inside the jt-translate module instead of the more general AffineOpImage making the computation faster.
The AffineNearestOpImage,AffineBilinearOpImage and AffineBicubicOpImage are similar, they only differs for the interpolation type used. They all override the computeRect() method of the AffineOpImage. When this method is called, it takes the Raster source data and the WritableRaster destination data and put them inside the related RasterAccessor; if ROI is used, even its RasterAccessor is created. These RasterAccessors together with the destination image bounds are all used as parameters for the 6 different Loop() (one for every image dataType) methods, in which the interpolation is performed. Every Loop() method extracts the image data as arrays of pixel for every band and then starts a loop on all the destination image positions. For every destination positions the source pixel positions are calculated iteratively by updating the initial pixel position on every row. From the source pixel position it is possible to find all the kernel pixels used for calculating the destination pixel value. During the calculation the presence of no Data is taken into account by associating a weight to every kernel pixel. The weight can be set to 0 if the related pixel is a No Data, otherwise it is set to 1. If all the kernel pixels have 0 weight, the related destination pixel value is set to the special value Destination No Data, choosen by the user at the interpolation creation time. An eventual ROI is considered during calculation by setting the destination pixel to the Destination No Data value only if all the kernel pixels are outside the ROI, otherwise the calculation takes into account even the Out-Of-ROI kernel pixels.
The AffineGeneralOpImage behaviour is similar but it delegates the calculation of the destination pixel to the interpolator used.
A simple code for better understanding the affine operation:
// destNumBand = destination image band number
// minYpositionIndex, minXpositionIndex = destination image minimum index values on the source array
// destHeight, destWidth = destination image dimensions
// sourceDataArray, destinationDataArray = source and destination image data array
// ROI = possible roi object
// noDataRange = possible range of source no data
// transform = affine transformation
for(int k = 0; k < destNumBand; k++){
for(int j = minYpositionIndex; j < destHeight; j++){
for(int i = minXpositionIndex; i < destWidth; i++){
//Creation of the interpolation kernel
int[][] sourceKernel = sourceDataArray[k][kernel indexes];
// Pixel interpolation with the associated kernel
destinationDataArray[k][i + j] = interpolate(sourceKernel, transform, ROI, noDataRange);
updateXYPositions();
}
}
}
For testing the functionalities of the classes described above, 5 J-Unit test-classes have been created:
- NearestAffineTest.java
- BilinearAffineTest.java
- BicubicAffineTest.java
- ImageRGBTest.java
- CoverageClassTest.java
- ComparisonTest.java
The first 3 classes execute the affine transformation with the 3 different kind of interpolation on synthetic images of different data types(even on binary images). The 4th class tests the functionality of the affine operation on an RGB image. The 5th class tests the functionality of the AffineDescriptor and of the AffineCRIF classes. These 5 classes extends the class TestAffine.java (which is on turn an extension of the TestBase.java class located in the jt-utilities project). For the first 4 test classes it is possible to set various parameters for seeing the output result of the affine operation:
- JAI.Ext.Interactive boolean parameter, must be set to true for printing the output image to the screen.
- JAI.Ext.ImageFill boolean parameter, must be set to true if the source image must be filled with values different from 0.
- JAI.Ext.InverseScale integral parameter, if set to 0 the image dimensions are increased, if set to 1 decreased.
- JAI.Ext.TestSelector integral parameter, for the first 3 test-classes it indicates which test to see(from 0 to 4), for the 4th test-class it indicates the type of interpolation used (from 0 to 2).
- JAI.Ext.TransformationSelector integral parameter, it indicates which kind of transformation is printed, 0 for rotation, 1 for scaling, 2 for translation, 3 for a combination of them.
The last test-class compares the computation time between the old and the new AffineDescriptor without ROI or No Data. This test is executed by repeatedly calling the PlanarImage.getTiles() method for every cycle and saving the calculation time. At the end of every cycle the JAI TileCache is flushed so that all the image tiles must be recalculated. The average, maximum and minimum computation time are not saved for all the iterations, because the first N iterations are not considered due to the Java HotSpot compilation. The number of the iterations to consider and not can be set by passing respectively these 2 Integer parameters to the JVM: JAI.Ext.BenchmarkCycles and JAI.Ext.NotBenchmarkCycles. For selecting which of the 2 descriptors must be tested, the JAI.Ext.OldDescriptor JVM parameter must be set to true or false(true for the old descriptor and false for the new one); like in the 4th test-class, the other two JVM parameters JAI.Ext.TestSelector and JAI.Ext.TransformationSelector should be passed. If the native acceleration should be used, then the JAI.Ext.Acceleration JVM parameter must be set to true. The statistics are print to the screen at the end of the process.