-
Notifications
You must be signed in to change notification settings - Fork 0
New JAI Mosaic Operation
In this page will be described a modified implementation of the JAI Mosaic Operation that supports noData values and doesn't use threshold values. In this new Mosaic class the threshold is not used because it doesn't fit our requests.
NoData values are special values like -9999, Integer. MIN_VALUE , etc... which indicates the absence of informations in that pixel. There is no standard for these values so in every image there could be a different type of noData. In the old Mosaic operation behaviour, one way to avoid a single type of noData value was to set a specific threshold(equal to the noData value) and any value below it was considered a background value. If there were multiple images with multiple different noData values, the above behaviour could not be correct. For example, if there are 2 images with 2 different noData value types, the threshold could be set to the bigger value but, in this way, even the image pixel of the lowest noData value image that lays below the selected threshold are set to background value, unless are set two different threshold per image.
A simple way to avoid all of these inconvenients is to set an initial range of noData values to check when the Mosaic operation is performed. This multiple values are saved into an array of Range objects or in a singular Range objects, if they are contiguous. The new Mosaic operation should take this array of values and then use it for checking if there is noData values and handle them.
A code example may be useful:
// MOSAIC TYPE OVERLAY
// s[i][x][y] = pixel value for the source i
// d[x][y] = pixel value of the destination
d[x][y] = destinationNoData;
for(int i=0; i< sources.length(); i++){
if(!SourceNoDataRange[i].contains(s[i][x][y]){
d[x][y] = s[i][x][y];
break;
}
}
// MOSAIC TYPE BLEND
// s[i][x][y] = pixel value for the source i
// w[i][x][y] = weigthed value of the destination
// d[x][y] = pixel value of the destination
w[i][x][y] = 0;
d[x][y]=0;
int numerator=0;
int denominator=0;
for(int i=0; i< sources.length(); i++){
if(!SourceNoDataRange[i].contains(s[i][x][y]){
w[i][x][y] = 1;
}
numerator+=w[i][x][y]*s[i][x][y];
denominator+=w[i][x][y];
}
d[x][y]=numerator/denominator;
//
The class described below are in the it.geosolutions.jaiext.mosaic package.
The new Mosaic OpImage class extends the OpImage abstract class and overrides his methods:
- the OpImage Constructor: this method makes some controls on the input data and then initialize the state variables.
- mapDestRect: this method calculates the intersection between one of the source images and the destination raster bounds.
- mapSourceRect: Similar to the method above, calculates the intersection between one of the source image and the source raster bounds.
- computeTile: This method is the most important to overrides because it calculates the mosaic operation in the selected raster and returns the new image mosaic tile.
The MosaicOpImage Constructor firstly recalls the OpImage constructor for the OpImage initialization. In this phase, a static method called checkLayout performs a control on the input layout and the source images for checking if the sample model, the band number and the data type of all the images are the same and then it performs an union of all the images bounds. After the layout control,the destination no data values are set. These objects are used when the source image pixels in the same location are no data, doesn't fall in an eventual ROI and has the related alpha value set to 0. Then a for cycle over the entire source image list monitors the presence or the absence of the ROIs, alpha channels and No Data Ranges. If the alpha channel is present for a source image, the cycle verifies that the alpha channel has the same band number, data type and sample size of all the images; if no data ranges are present and the image data type is Byte, then a lookup table is created for quicly store all the possible output data, else if the data type is Float or Double, then it is checked if the range contains NaN or Infinite values. The last operation in the constructor is the border extender creation. This object is used for extending the image borders if the destination rectangle falls outside the source images or the ROIs and alpha channels bounds. The MosaicOpImage input parameters (in addition to the source images) are: a java Bean(called ImageMosaicBean) containing all the image informations like ROI, alpha channel,source no data; an array of the destination no data values which length is equal to the band number; the mosaic type selected (OVERLAY or BLEND).
The Second method simply checks if the destination raster is present or if the source image index is between 0 and the source number and, if true, intersects their bounds. The Third method checks the source image rectangle and the source image index and then calculates the intersection.
The computeTile method is fundamental to override because it performs the mosaic operation on a single tile. The input data of this method are the tile X and Y indexes in the image Tile grid. From these data a new destination image writable raster is selected to be filled with the source images pixels. Then for every source image 4 arrays are used for storing all the source raster informations. The storing operation is done through a cycle that save in the related arrays the source raster, an eventual no data Range and if present, image ROI and alpha channel for the selected raster. When this operation is completed, the mosaic operation is computed by calling the computeRect method. When the destination raster is calculated, the program checks if the source raster overlaps multiple tiles and, in that case, performs tile recycling (if the TileRecycler is set). After all this operations, the computed tile is returned.
ComputeRect is a private method that takes in input the 4 calculated arrays, the destination raster and a rectangle containing the destination bounds. If the source image number is lower than 1, the computeRect method returns the destination raster filled with destination no data values. Otherwise it finds the best compatible formatTagID for creating a rasterAccessor for any source raster and even for the destination raster. The source rasterAccessors are saved into a java bean(called rasterBeanAccessor) array with the related informations(ROI, alpha channel, source no data) and passed to one of the 6 Loop (the loops are more than 1 because they differ for the data type) method which saves the results of the mosaic operation in the destination rasterAccessor that will copy the mosaic data in the related raster.
Every Loop method is the real place where the mosaic operation is performed. This method can be divided in 2 phases: initialization and calculation. In the first one, all the data arrays are created for all the bands and for every source. From the rasterAccessors it is possible to retrieve the line offset and stride, pixel offset and stride and the band offsets. These data are important for iterating around the image pixels. After having set all this parameters, the method select the weight type used. This parameter is important because it changes the way the source data are taken. It can be one of this types: WEIGHT_TYPE_ALPHA which checks the alpha channel value of that pixel; WEIGHT_TYPE_ROI which checks if the pixel is inside the ROI; WEIGHT_TYPE_NO_DATA which simply checks whether the pixel value is contained or not in the no data Range. In the second phase the Loop method iterates over every pixel of all the bands and sources and calculates the mosaic of the source images pixel in the same (x,y) location. For achieving this goals some step must be done. The first operation to do is to extract the pixel value from the tile and control if it is a no Data. If this condition is verified, the above source pixel is not considered in the mosaic operation and the method continues with the pixel of the following sources(in the same location). If the previous condition is not verified, the pixel is evaluated following the weight type selected before for that image. The first pixel that passes the evaluation is set as the destination pixel value and the method jumps to the another pixel of the first source in a different location, otherwise it select the pixel value of the next source with in the same location. If no pixel passes the evaluation, then the destination pixel value takes the destination no data value. The workflow described above descripts only the mosaic operation in the OVERLAY mode. In the BLEND mode every pixel source in the same location are evaluated in a similar way (it doesn't jump to a new location if the evaluation is not passed) but for every pixel is assigned a weigth value and the destination value is calculated as a weighted sum of the source pixel(See the code above).
The MosaicOpImage.java class contains all the operation described but it is not the only one class requested for the mosaic operation. Other classes are:
- ImageMosaicBean.java
- MosaicDescriptor.java
- MosaicRIF.java
- MosaicOPImage2.java
The first class is a java Bean used for setting and getting all the image informations in one single object.
The second class is an extension of the OperationDescriptorImpl.java class containing the information about the MosaicOpImage, like the default values, class names and resources. The validateParameter() method is used for evaluating if the input parameter are correct for the operation. The isRenderable method informs that the image does not support the renderable mode.
The third class is an implementation of the RenderedImageFactory interface and is used for giving the possibility to create the mosaic RenderedImage by giving an input parameterBlock and calling the JAI.create method. This last operation requests that the Mosaic operation is registered to a new registryFile.jai (located into the META_INF directory) inside the project with a univoke name.
The last class is an older implementation of the MosaicOpImage that is not used because it has worst performance than that of the MosaicOpImage class.
In the source floder src/test/java there are 3 test class:
- MosaicTestImage.java
- ComparisonTest.java
- MosaicTest.java
The first class calculates the mosaic operation on 2 image. This test-class allows the user to choose the new or the old version of the MosaicOpImage by setting in input to the JVM the JAI.Ext.NewDescriptor boolean parameter. For printing hte result to the screen the JAI.Ext.Interactive parameter must be set to true.
The second class compares the computation time between the 2 versions of the MosaicOpImage. The test cyclically takes an already existent image, gives it to the MosaicDescriptor (Old or New version) and call the PlanarImage.getTiles() method (in this way all the image tiles are calculated). The computation time is calculated only when the getTiles() method is called. At the end of the cycle the JAI TileCache is flushed, so that all the image tiles must be recalculated every time. The average, maximum and minimum computation times are not stored for all the iterations, because the first N iterations are not considered due to the Java HotSpot compilation. The number of the iteration to consider and not can be set by passing respectively these 2 Integer parameters to the JVM: JAI.Ext.BenchmarkCycles and JAI.Ext.NotBenchmarkCycles. If the descriptor to use is the old one, the user must set to true the JVM parameter JAI.Ext.OldDescriptor, otherwise the new descriptor is used. The mosaic type is by default OVERLAY, but if the JVM boolean parameter JAI.Ext.MosaicBlend is set to true, then the BLEND mosaic type is used. 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.
The last class executes many tests on the new MosaicOpImage class. These tests consist in a mosaic of 2 or 3 images with different kind of values. The Mosaic type used is OVERLAY.
For 2 images:
- image 1 with no data and image 2 with no data.
- image 1 with no data and image 2 with valid data.
- image 1 with valid data and image 2 with valid data.
- image 1 with no data and image 2 with ROI and valid data.
- image 1 with valid data and image 2 with ROI and valid data.
- image 1 with no data and image 2 with Alpha channel and valid data.
- image 1 with valid data and image 2 with Alpha channel and valid data.
For 3 images:
- image 1 with no data, image 2 with ROI and valid data, image3 with Alpha channel and valid data.
- image 1 with valid data, image 2 with valid data, image3 with valid data but no data Range selected.
This test are executed for images with 1 and 3 bands and for data type: Byte, Short, Ushort, Integer, Float, Double. For the BLEND Mosaic type some tests have been performed with 3 images: the first with no data, the second with a ROI and the third with an alpha channel. For every image some pixels are taken and their values are compared to the expected ones. Even the exceptions are tested.