Skip to content

Scale Operation

n-lagomarsini edited this page Jun 24, 2014 · 1 revision

This implementation of the JAI Scale operation supports ROI and No Data values in the source image.

WORKFLOW:

  • creation of new interpolators of type Nearest-Neighbor, Bilinear and Bicubic supporting ROI and no data values. These interpolators can be used for every operatio, not only the scale.
  • creation of various JUnit test-file for checking all the functionality of this operation. These test-files try all the interpolation type for an image and then check if the operation has been done in the correct way.
  • development of a new JAI ScaleOpImage class to test with the previous test-file, the related Descriptor and RenderedImageFactory. These classes substitute the old JAI scale classes

The classes used by the new scale operations are:

  • ScaleOpImage.java : this class is similar to the old ScaleOpImage.java class but has in addiction two other methods called preComputePositionsInt() and preComputePositionsFloat(), used for mapping the destination image into the source image point grid.
  • ScaleDescriptor.java : this class describes the parameters used by the ScaleOpImage. Also it gives the possibility to validate the input parameters and to extends the ROI data with the same scale parameters used on the image (with the getProperty() method).
  • ScaleCRIF.java : this class is the RenderedImageFactory used by JAI for executing the scale operation when the method JAI.create("ScaleNoData") is called.
  • ScaleNearestOpImage : this class extends the ScaleOpImage class and is used when the Nearest interpolation is need.
  • ScaleBilinearOpImage : this class extends the ScaleOpImage class and is used when the Bilinear interpolation is need.
  • ScaleBicubicOpImage : this class extends the ScaleOpImage class and is used when the Bicubic/Bicubic2 interpolation is need.
  • ScaleGeneralOpImage : this class extends the ScaleOpImage class and is used when the Interpolation type is different from the 3 above or the image is Binary.
  • ScaleNearestOpImage2 and ScaleNearestOpImage3 : this two class are not used because they have worst performances than the ScaleNearestOpImage class but are kept in the project for future modifications.

The first class is an abstract class which executes some initialization operation that are common for all kind of interpolation.

The 2nd and 3rd classes are used when a new scale operation is called: the descriptor is used for packing all the input parameters inside a parameterBlock and calling the JAI.create("ScaleNoData") method. When this method is called, the JAI api searches for the RenderedImageFactory associated with the "ScaleNoData" operation and then finds the ScaleCRIF class, which sequentially calls one of the subclass of the ScaleOpImage and then the scale operation could be done.

For translation operations on a interval and without ROI, the ScaleCRIF class calls the TranslateIntOpImage.java class from the jt-translate project that performs this operation in a faster way by simply calling the tile.createTranslatedChild(tileXindex,tileYindex). Also if the operation is simply a copy of the source image, then the CopyOpImage.java class is called.

The ScaleNearestOpImage, ScaleBilinearOpImage and ScaleBicubicOpImage are similar. They all override the computeRect() method of the ScaleOpImage class. 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. After this step, the destination image pixel positions are precalculated from the source positions with the ScaleOpImage accessor methods; these values could have a fractional part if the destination image pixels are not coincident with the old image ones. These values and the RasterAccessor 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 data for every band and then starts a loop on all the destination image positions. For every positions the destination pixel value is calculated from a kernel of source pixel: 1x1 for Nearest interpolation, 2x2 for Bilinear interpolation and 4x4 for Bicubic or Bicubic2 interpolation. 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 are 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 ScaleGeneralOpImage behaviour is similar but it delegates the calculation of the destination pixel to the interpolator used.

A simple code for better understanding the scale 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
// sourceKernel = kernel used for interpolating pixels

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, ROI, noDataRange);
        } 
    }
}

The tests that checks the classes described above are these:

  • NearestTest.java
  • BilinearTest.java
  • BicubicTest.java
  • ImageRGBTest.java
  • CoverageClassTest.java
  • ComparisonTest.java

The first 3 tests perform the scale operation with the 3 different kind of interpolation on 3 band images of all types, with and without ROI, with and without No Data. The 4th class performs a scale operation with all of the interpolation type on a RGB real image with ROI but without No Data. The 5th test class is used for testing the functionality of the already described TranslateIntOpImage class and CopyOpImage class called by ScaleCRIF and that of the ScaleDescriptor getProperty() method. These 5 classes are extension of the class TestScale.java (which is in turn an extension of the abstract class TestBase.java in the jt-utilities module), in which are defined the test methods and image creations methods that are called by the related subclasses. 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 classes it indicates which test to see(from 0 to 4), for the 4th class it indicates the type of interpolation used (from 0 to 2).

The last test makes a comparison between the old and the new version of the ScaleDescriptor without ROI or No Data. The test repeatedly executes the scale operation on all the tiles of an RGB image by calling the getTiles() method and then updates the average computational time. At the end of every cycle the JAI TileCache is flushed so that all the image tiles must be recalculated every time. The average, maximum and minimum computation time are not updated 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 JVM parameters JAI.Ext.TestSelector and JAI.Ext.ImageReduction (as a boolean) 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.