-
Notifications
You must be signed in to change notification settings - Fork 37
ZonalStats Operation
This operation is a new version of the JaiTools ZonalStats operation supporting calculation on multiple zones. The ZonalStats operation consists of calculating various statistics on a list of geometries on an image. Optionally the user can supply a classifier image for computing statistics for each defined class.
WORKFLOW:
- creation of the ZoneGeometry object used for storing the statistics for each geometry
- creation of the ZonalStatsOpImage, descriptor and RenderedImageFactory for executing the ZonalStats operation
- Testing of the previously written classes.
The main differences between the JaiTools ZonalStats and this new implementation are:
- the possibility to insert in input a list of geometries and calculate their statistics only one time instead of repeating the same operation on all the list of geometries like in the previous implementation.
- the support for JAI inner multithreading.
- the presence of a more performant Range class, instead of the JaiTools implementation.
The classes inside this module are:
- ZonalStatsOpImage.java : this class performs the ZonalStats operation.
- ZonalStatsDescriptor.java : this class describes the operation and the input parameters for the ZonalStatsOpImage class.
- ZonalStatsRIF.java : this class is a RenderedImageFactory associated to the ZonalStats operation called by the JAI.create() method.
- ZoneGeometry.java : this class saves the statistics results for every geometry supplied.
The ZonalStatsOpImage class is the place where all the statistics computations are performed. Before the calculations, a union of all the geometries is calculated for reducing the computations to only this active area. With this trick only the tiles inside the union bounds are considered and the others are skipped. For each active tile, all the pixels are evaluated. The pixel evaluation consists of searching with a spatial index the geometries on which the pixel is contained and then updating(in a synchronized block) the ZoneGeometry instances related to the following geometries. If the classifier image is present, then the related statistics objects inside every ZoneGeometry instance are updated. If a Classifier is not present, but a List of Ranges are defined, then the statistics can be calculated for each one of these ranges if the localStats variable is set to true, otherwise the statistics are calculated globally for each zones but only for pixel values inside the ranges. This workflow has been created for supporting a multithreaded environment. If No Data Range or ROI are passed as input, they are handled by calculating only those pixels which are not a No Data or fall inside the ROI.
The ZonalStatsDescriptor class is used for describing the input parameters used by the ZonalStatsOpImage and its functionality. Also it provides a simple static method called create which takes the input parameters, groups them and finally send in input to the JAI.create() method. This method call the RenderedImageFactory associated with the ZonalStats operation, ZonalStatsRIF. The ZonalStatsRIF class has only one method called create which unpacks the input the parameterBlock and then returns a new instance of the ZonalStatsOpInage with the input parameters.
The ZoneGeometry class is an object containing the statistics for the associated geometry. The global container is a Map where every index is a band and the associated value is another Map object. This second Map object has a user-defined Range as index and another Map as value. The last map contains a mapping for all the classes indicated by the classifier image, and each class index is associated to a Statistics array. With various utility method it is possible to retrieve all of these objects.
A simple pseudo-code for better understanding the ZonalStats operation.
// s[x][y] = pixel value of the source.
// zoneGeomObj = array of the geometries statistics container.
// spatial = spatial index
// tile = array containing all the tiles
// selectedBand = index for the band where the operation is performed
// class = index for the class defined by the classifier
// geometries = list of geometries returned by the spatial index
// validData = boolean indicating that the value is not a No Data.
// insideROI = boolean indicating that the values is inside the ROI.
// numTiles,srcHeight,srcWidth = source image tiles number, height, width.
// union = union of all the geometries
for(int t = 0; t<numTiles;t++){
if(union.contains(tile[t].bounds())){
for(int y = 0; y<srcHeight;y++){
for(int x = 0; x<srcWidth;x++){
List geometries = spatial.query(x,y);
int class = classifier.getSample(x,y);
for(int i = 0; i < geometries.length; i++){
if(validData && insideROI){
zoneGeomObj[i].add(s[x][y],selectedBand,class);
}
}
}
}
}
}
//Result for the Class 0 and the ZoneGeometry 0, supposing the first element of the array stores the Mean value
double mean = (Double)zoneGeomObj[0].getStatsPerBandNoRange(selectedBand,0)[0].getResult();
For testing the classes described above, two JUnit test-classes have been developed:
- ZonalStatsTest.java
- ComparisonTest.java
The first class checks if the ZonalStats operation is performed in the correct way. This purpose is achieved by comparing the result of the calculations with pre-calculated statistics on the same image. The tests are made with and without No Data, with and without ROI. The user can choose if an optional classifier class should be created by setting to true the JVM boolean parameter JAI.Ext.Classifier.
The second class compares the old JaiTools ZonalStats operation with the new one. This test is made by calling many times the getProperty() method for one of the two operators and saving the mean, maximum and minimum computation 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 descriptor associated to the operation 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); the JVM parameter JAI.Ext.TestSelector can be from 0 to 5 and indicates the image data types; the presence of the classifier can be indicated with the JAI.Ext.Classifier boolean parameter. Finally, if the user wants to add the NoData control, the JAI.Ext.RangeUsed parameter must be set to true. The computation times are print to the screen at the end of the process.