/* JAI-Ext - OpenSource Java Advanced Image Extensions Library
* http://www.geo-solutions.it/
* Copyright 2014 GeoSolutions
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.geosolutions.jaiext.stats;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import javax.media.jai.ROI;
import javax.media.jai.RasterAccessor;
import javax.media.jai.RasterFormatTag;
import javax.media.jai.iterator.RandomIter;
import it.geosolutions.jaiext.iterators.RandomIterFactory;
import it.geosolutions.jaiext.range.Range;
import it.geosolutions.jaiext.stats.Statistics.StatsType;
/**
* The ComplexStatsOpImage class performs various comples statistics operations on an image. The statistical operation are indicated by the
* {@link StatsType} class. A comples operation is an operation which stores the pixel values into an array. These operations can be calculated
* together by adding entries in the definition array "statsTypes". A ROI object passed to the constructor is taken into account by counting only the
* samples inside of it; an eventual No Data Range is considered by counting only values that are not No Data. The statistical calculation is
* performed by calling the getProperty() method. The statistics are calculated for every image tile but the results are saved into only one global
* container. For avoiding to compromise the thread-safety of the class, every statistics object should handle concurrent threads. At the end of the
* calculation the statistics container is passed to the getProperty() method as a Result. For avoiding unnecessary operations the statistics can be
* calculated only the first time; but if the user needs to re-calculate the statistics, they can be cleared with the clearStatistic() method and then
* returned by calling again the getProperty() method.
*/
public class ComplexStatsOpImage extends StatisticsOpImage {
public ComplexStatsOpImage(RenderedImage source,
int xPeriod, int yPeriod, ROI roi, Range noData, boolean useROIAccessor, int[] bands,
StatsType[] statsTypes, double[] minBound, double[] maxBound, int[] numBins) {
super(source, xPeriod, yPeriod, roi, noData, useROIAccessor, bands,
statsTypes, minBound, maxBound, numBins);
// Storage of the statistic types indexes if present, and check if they are not simple statistic
// objects like Mean
if (statsTypes != null) {
for (int i = 0; i < statsTypes.length; i++) {
if (statsTypes[i].getStatsId() < 6) {
throw new IllegalArgumentException("Wrong statistic types");
}
}
} else {
throw new IllegalArgumentException("Statistic types not present");
}
this.statsTypes = statsTypes;
// Number of statistics calculated
this.statNum = statsTypes.length;
// Storage of the band indexes and length
this.bands = bands;
int[] numB = new int[bandsNumber];
double[] lowValue = new double[bandsNumber];
double[] highValue = new double[bandsNumber];
for (int b = 0; b < bandsNumber; b++) {
numB[b] = numBins.length == 1 ? numBins[0] : numBins[b];
lowValue[b] = minBound.length == 1 ? minBound[0] : minBound[b];
highValue[b] = maxBound.length == 1 ? maxBound[0] : maxBound[b];
}
// Creation of a global container of all the selected statistics for every band
this.stats = new Statistics[selectedBands][statNum];
// Filling of the container
for (int i = 0; i < selectedBands; i++) {
for (int j = 0; j < statNum; j++) {
stats[i][j] = StatsFactory.createComplexStatisticsObjectFromInt(
statsTypes[j].getStatsId(), lowValue[i], highValue[i], numB[i]);
}
}
}
/**
* Returns a tile for reading.
*
* @param tileX The X index of the tile.
* @param tileY The Y index of the tile.
* @return The tile as a <code>Raster</code>.
*/
public Raster computeTile(int tileX, int tileY) {
// STATISTICAL ELABORATIONS
// selection of the format tags
RasterFormatTag[] formatTags = getFormatTags();
// Selection of the RasterAccessor parameters
Raster source = getSourceImage(0).getTile(tileX, tileY);
// Control if the Period is bigger than the tile dimension, in that case, the
// statistics are not updated
if (xPeriod > getTileWidth() || yPeriod > getTileHeight()) {
return source;
}
Rectangle srcRect = getSourceImage(0).getBounds().intersection(source.getBounds());
// creation of the RasterAccessor
RasterAccessor src = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0)
.getColorModel());
// ROI calculations if roiAccessor is used
RasterAccessor roi = null;
RandomIter roiIter = null;
if (useROIAccessor) {
// Note that the getExtendedData() method is not called because the input images are padded.
// For each image there is a check if the rectangle is contained inside the source image;
// if this not happen, the data is taken from the padded image.
Raster roiRaster = null;
if(srcROIImage.getBounds().contains(srcRect)){
roiRaster = srcROIImage.getData(srcRect);
}else{
roiRaster = srcROIImgExt.getData(srcRect);
}
// creation of the rasterAccessor
roi = new RasterAccessor(roiRaster, srcRect, RasterAccessor.findCompatibleTags(
new RenderedImage[] { srcROIImage }, srcROIImage)[0],
srcROIImage.getColorModel());
} else if(hasROI) {
roiIter = RandomIterFactory.create(srcROIImage, srcROIImage.getBounds(), true, true);
}
// Computation of the statistics
switch (src.getDataType()) {
case DataBuffer.TYPE_BYTE:
byteLoop(src, srcRect, roi, stats, roiIter);
break;
case DataBuffer.TYPE_USHORT:
ushortLoop(src, srcRect, roi, stats, roiIter);
break;
case DataBuffer.TYPE_SHORT:
shortLoop(src, srcRect, roi, stats, roiIter);
break;
case DataBuffer.TYPE_INT:
intLoop(src, srcRect, roi, stats, roiIter);
break;
case DataBuffer.TYPE_FLOAT:
floatLoop(src, srcRect, roi, stats, roiIter);
break;
case DataBuffer.TYPE_DOUBLE:
doubleLoop(src, srcRect, roi, stats, roiIter);
break;
}
return source;
}
}