/* 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 it.geosolutions.jaiext.iterators.RandomIterFactory; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.stats.Statistics.StatsType; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.RasterAccessor; import javax.media.jai.RenderedOp; import javax.media.jai.iterator.RandomIter; import com.sun.media.jai.util.PropertyUtil; /** * This abstract class is used for defining the common methods for calculating image statistics. There are 2 subclasses of this class called * {@link SimpleStatsOpImage} and {@link ComplexStatsOpImage}. The first one is used for calculating simple statistics which does not requests the use * of an array for saving all the values, instead of the second class which stores the statistics on an array and then performs the final computations * when the result is requested. The 2 subclasses must only update their constructor for adding a control on which kind of statistics are calculated * and defining the computeTile() method which is used for calculating the statistics for each tile. */ public abstract class StatisticsOpImage extends OpImage { /** ROI extender */ protected final static BorderExtender ROI_EXTENDER = BorderExtender .createInstance(BorderExtender.BORDER_ZERO); /** Object containing the current statistics for the selected bands and for the selected statistic types */ protected Statistics[][] stats; /** Boolean indicating if a No Data Range is used */ protected final boolean hasNoData; /** Boolean indicating if a ROI object is used */ protected final boolean hasROI; /** Boolean indicating if a ROI RasterAccessor should be used */ protected final boolean useROIAccessor; /** ROI image */ protected final PlanarImage srcROIImage; /** Rectangle containing ROI bounds */ protected final Rectangle roiBounds; /** Boolean indicating that there No Data and ROI are not used */ protected final boolean caseA; /** Boolean indicating that only ROI is used */ protected final boolean caseB; /** Boolean indicating that only No Data are used */ protected final boolean caseC; /** Image bands number */ protected int bandsNumber; /** Selected bands number */ protected int selectedBands; /** Array containing the indexes of the selected bands */ protected int[] bands; /** Array containing the type of statistics to calculate */ protected StatsType[] statsTypes; /** Length of the statsTypes array */ protected int statNum; /** Boolean indicating if the statistics have been already calculated(if false) or not */ protected AtomicBoolean firstTime = new AtomicBoolean(true); /** Horizontal subsampling */ protected final int xPeriod; /** Vertical subsampling */ protected final int yPeriod; /** Boolean lookuptable used if no data are present */ protected final boolean[] booleanLookupTable = new boolean[256]; /** No Data Range */ protected Range noData; /** Extended ROI image*/ protected RenderedOp srcROIImgExt; public StatisticsOpImage(RenderedImage source, int xPeriod, int yPeriod, ROI roi, Range noData, boolean useROIAccessor, int[] bands, StatsType[] statsTypes, double[] minBound, double[] maxBound, int[] numBins) { super(vectorize(source), new ImageLayout(source), null, true); // Source Image bands bandsNumber = source.getSampleModel().getNumBands(); // Selected Bands array dimension int selectedBands = bands.length; // BAND INDEX CONTROL if (selectedBands > bandsNumber) { throw new IllegalArgumentException( "Number of Bands to analyze cannot be more than the Image bands"); } else if (selectedBands <= 0) { throw new IllegalArgumentException( "Number of Bands to analyze cannot be less or equal to 0"); } else { for (int i = 0; i < selectedBands; i++) { if (bands[i] > bandsNumber) { throw new IllegalArgumentException( "Band index cannot be more than the Image bands"); } if (bands[i] < 0) { throw new IllegalArgumentException("Band index cannot be less than 0"); } } } // definition and check of the sampling parameters and position parameters if (xPeriod < 1 || yPeriod < 1) { throw new UnsupportedOperationException("Oversampling cannot be calculated"); } this.xPeriod = xPeriod; this.yPeriod = yPeriod; // Storage of the band indexes and length this.bands = bands; this.selectedBands = selectedBands; // Check if No Data control must be done if (noData != null) { hasNoData = true; this.noData = noData; } else { hasNoData = false; } // Check if ROI control must be done if (roi != null) { hasROI = true; // Roi object ROI srcROI = roi; // Creation of a PlanarImage containing the ROI data srcROIImage = srcROI.getAsImage(); // Source Bounds Rectangle srcRect = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); // Padding of the input ROI image in order to avoid the call of the getExtendedData() method // ROI bounds are saved roiBounds = srcROIImage.getBounds(); int deltaX0 = (roiBounds.x - srcRect.x); int leftP = deltaX0 > 0 ? deltaX0 : 0; int deltaY0 = (roiBounds.y - srcRect.y); int topP = deltaY0 > 0 ? deltaY0 : 0; int deltaX1 = (srcRect.x + srcRect.width - roiBounds.x + roiBounds.width); int rightP = deltaX1 > 0 ? deltaX1 : 0; int deltaY1 = (srcRect.y + srcRect.height - roiBounds.y + roiBounds.height); int bottomP = deltaY1 > 0 ? deltaY1 : 0; // Extend the ROI image ParameterBlock pb = new ParameterBlock(); pb.setSource(srcROIImage, 0); pb.set(leftP, 0); pb.set(rightP, 1); pb.set(topP, 2); pb.set(bottomP, 3); pb.set(ROI_EXTENDER, 4); srcROIImgExt = JAI.create("border", pb); // The useRoiAccessor parameter is set this.useROIAccessor = useROIAccessor; } else { hasROI = false; this.useROIAccessor = false; roiBounds = null; srcROIImage = null; } // Creation of a lookuptable containing the values to use for no data if (hasNoData && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE) { for (int i = 0; i < booleanLookupTable.length; i++) { byte value = (byte) i; booleanLookupTable[i] = !noData.contains(value); } } // Definition of the possible cases that can be found // caseA = no ROI nor No Data // caseB = ROI present but No Data not present // caseC = No Data present but ROI not present // Last case not defined = both ROI and No Data are present caseA = !hasNoData && !hasROI; caseB = !hasNoData && hasROI; caseC = hasNoData && !hasROI; } /** * Returns a list of property names that are recognized by this image. * * @return An array of <code>String</code>s containing valid property names. */ public String[] getPropertyNames() { // Get statistics names and names from superclass. String[] statsNames = new String[] { Statistics.STATS_PROPERTY }; String[] superNames = super.getPropertyNames(); // Return stats names if not superclass names. if (superNames == null) { return statsNames; } // Check for overlap between stats names and superclass names. List extraNames = new ArrayList(); for (int i = 0; i < statsNames.length; i++) { String prefix = statsNames[i]; String[] names = PropertyUtil.getPropertyNames(superNames, prefix); if (names != null) { for (int j = 0; j < names.length; j++) { if (names[j].equalsIgnoreCase(prefix)) { extraNames.add(prefix); } } } } // If no overlap then return. if (extraNames.size() == 0) { return superNames; } // Combine superclass and extra names. String[] propNames = new String[superNames.length + extraNames.size()]; System.arraycopy(superNames, 0, propNames, 0, superNames.length); int offset = superNames.length; for (int i = 0; i < extraNames.size(); i++) { propNames[offset++] = (String) extraNames.get(i); } // Return combined name set. return propNames; } /** * This method is used if the user needs to perform again the statistical calculations. */ public synchronized void clearStatistic() { // Filling of the container for (int i = 0; i < stats.length; i++) { for (int j = 0; j < statNum; j++) { stats[i][j].clearStats(); } } // Setting the calculations to be performed firstTime.getAndSet(true); } /** * When the dispose method is called, then old dispose method is performed and also the statistic container is cleared. */ public void dispose() { if(srcROIImgExt != null) { srcROIImgExt.dispose(); } super.dispose(); clearStatistic(); } /** * Computes and returns all tiles in the image. The tiles are returned in a sequence corresponding to the row-major order of their respective tile * indices. The returned array may of course be ignored, e.g., in the case of a subclass which caches the tiles and the intent is to force their * computation. This method is overridden such that can be invoked only one time by using a flag for avoiding unnecessary computations. */ public Raster[] getTiles() { if (firstTime.getAndSet(false)) { if (hasROI) { return getTiles(getTileIndices(roiBounds)); } return getTiles(getTileIndices(getBounds())); } else { return null; } } /** * Get the specified property. * <p> * Use this method to retrieve the calculated statistics as an array per band and per statistic types. * * @param name property name * * @return the requested property */ @Override public Object getProperty(String name) { // If the specified property is "JAI-EXT.stats", the calculations are performed. if (Statistics.STATS_PROPERTY.equalsIgnoreCase(name)) { getTiles(); return stats.clone(); } else { return super.getProperty(name); } } protected void byteLoop(RasterAccessor src, Rectangle srcRect, RasterAccessor roi, Statistics[][] statArray, RandomIter roiIter) { // Source RasterAccessor initial positions int srcX = src.getX(); int srcY = src.getY(); final byte[] roiDataArray; final int roiScanLineInc; final int roiDataLength; if (useROIAccessor) { roiDataArray = roi.getByteDataArray(0); roiScanLineInc = roi.getScanlineStride() * yPeriod; roiDataLength = roiDataArray.length; } else { roiDataArray = null; roiScanLineInc = 0; roiDataLength = 0; } byte srcData[][] = src.getByteDataArrays(); int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // NO DATA AND ROI ARE NOT PRESENT if (caseA) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFF; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } // ONLY ROI IS PRESENT } else if (caseB) { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFF; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFF; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } // ONLY NO DATA ARE PRESENT } else if (caseC) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFF; // Control if the sample is Not a NO Data if (booleanLookupTable[sample]) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // BOTH NO DATA AND ROI ARE PRESENT } else { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFF; // Control if the sample is Not a NO Data if (booleanLookupTable[sample]) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFF; // Control if the sample is Not a NO Data if (booleanLookupTable[sample]) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } } } } protected void ushortLoop(RasterAccessor src, Rectangle srcRect, RasterAccessor roi, Statistics[][] statArray, RandomIter roiIter) { // Source RasterAccessor initial positions int srcX = src.getX(); int srcY = src.getY(); final byte[] roiDataArray; final int roiScanLineInc; final int roiDataLength; if (useROIAccessor) { roiDataArray = roi.getByteDataArray(0); roiScanLineInc = roi.getScanlineStride() * yPeriod; roiDataLength = roiDataArray.length; } else { roiDataArray = null; roiScanLineInc = 0; roiDataLength = 0; } short srcData[][] = src.getShortDataArrays(); int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // NO DATA AND ROI ARE NOT PRESENT if (caseA) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFFFF; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } // ONLY ROI IS PRESENT } else if (caseB) { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFFFF; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFFFF; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } // ONLY NO DATA ARE PRESENT } else if (caseC) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFFFF; // Control if the sample is Not a NO Data boolean isData = !noData.contains((short) sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // BOTH NO DATA AND ROI ARE PRESENT } else { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFFFF; // Control if the sample is Not a NO Data boolean isData = !noData.contains((short) sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]] & 0xFFFF; // Control if the sample is Not a NO Data boolean isData = !noData.contains((short) sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } } } } protected void shortLoop(RasterAccessor src, Rectangle srcRect, RasterAccessor roi, Statistics[][] statArray, RandomIter roiIter) { // Source RasterAccessor initial positions int srcX = src.getX(); int srcY = src.getY(); final byte[] roiDataArray; final int roiScanLineInc; final int roiDataLength; if (useROIAccessor) { roiDataArray = roi.getByteDataArray(0); roiScanLineInc = roi.getScanlineStride() * yPeriod; roiDataLength = roiDataArray.length; } else { roiDataArray = null; roiScanLineInc = 0; roiDataLength = 0; } short srcData[][] = src.getShortDataArrays(); int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // NO DATA AND ROI ARE NOT PRESENT if (caseA) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { short sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } // ONLY ROI IS PRESENT } else if (caseB) { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { short sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { short sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } // ONLY NO DATA ARE PRESENT } else if (caseC) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { short sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // BOTH NO DATA AND ROI ARE PRESENT } else { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { short sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { short sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } } } } protected void intLoop(RasterAccessor src, Rectangle srcRect, RasterAccessor roi, Statistics[][] statArray, RandomIter roiIter) { // Source RasterAccessor initial positions int srcX = src.getX(); int srcY = src.getY(); final byte[] roiDataArray; final int roiScanLineInc; final int roiDataLength; if (useROIAccessor) { roiDataArray = roi.getByteDataArray(0); roiScanLineInc = roi.getScanlineStride() * yPeriod; roiDataLength = roiDataArray.length; } else { roiDataArray = null; roiScanLineInc = 0; roiDataLength = 0; } int srcData[][] = src.getIntDataArrays(); int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // NO DATA AND ROI ARE NOT PRESENT if (caseA) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } // ONLY ROI IS PRESENT } else if (caseB) { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } // ONLY NO DATA ARE PRESENT } else if (caseC) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // BOTH NO DATA AND ROI ARE PRESENT } else { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { int sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } } } } protected void floatLoop(RasterAccessor src, Rectangle srcRect, RasterAccessor roi, Statistics[][] statArray, RandomIter roiIter) { // Source RasterAccessor initial positions int srcX = src.getX(); int srcY = src.getY(); final byte[] roiDataArray; final int roiScanLineInc; final int roiDataLength; if (useROIAccessor) { roiDataArray = roi.getByteDataArray(0); roiScanLineInc = roi.getScanlineStride() * yPeriod; roiDataLength = roiDataArray.length; } else { roiDataArray = null; roiScanLineInc = 0; roiDataLength = 0; } float srcData[][] = src.getFloatDataArrays(); int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // NO DATA AND ROI ARE NOT PRESENT if (caseA) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { float sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } // ONLY ROI IS PRESENT } else if (caseB) { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { float sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { float sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } // ONLY NO DATA ARE PRESENT } else if (caseC) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { float sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // BOTH NO DATA AND ROI ARE PRESENT } else { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { float sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { float sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } } } } protected void doubleLoop(RasterAccessor src, Rectangle srcRect, RasterAccessor roi, Statistics[][] statArray, RandomIter roiIter) { // Source RasterAccessor initial positions int srcX = src.getX(); int srcY = src.getY(); final byte[] roiDataArray; final int roiScanLineInc; final int roiDataLength; if (useROIAccessor) { roiDataArray = roi.getByteDataArray(0); roiScanLineInc = roi.getScanlineStride() * yPeriod; roiDataLength = roiDataArray.length; } else { roiDataArray = null; roiScanLineInc = 0; roiDataLength = 0; } double srcData[][] = src.getDoubleDataArrays(); int srcWidth = src.getWidth(); int srcHeight = src.getHeight(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // NO DATA AND ROI ARE NOT PRESENT if (caseA) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { double sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } // ONLY ROI IS PRESENT } else if (caseB) { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { double sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { double sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } // ONLY NO DATA ARE PRESENT } else if (caseC) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { double sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } // BOTH NO DATA AND ROI ARE PRESENT } else { // ROI RasterAccessor is used if (useROIAccessor) { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // roi y position int posyROI = y * roiScanLineInc; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // ROI index position int windex = x * xPeriod + posyROI; // ROI value int w = windex < roiDataLength ? roiDataArray[windex] & 0xff : 0; // Control if the sample is inside ROI if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { double sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } // ROI RasterAccessor is not used } else { // Cycle on the y axis for (int y = 0; y < srcHeight; y += yPeriod) { // y position on the source data array int posy = y * srcScanlineStride; // Cycle on the x axis for (int x = 0; x < srcWidth; x += xPeriod) { // x position on the source data array int posx = x * srcPixelStride; // PixelPositions int x0 = srcX + x; int y0 = srcY + y; // Control if the sample is inside ROI if (roiBounds.contains(x0, y0)) { // ROI value int w = roiIter.getSample(x0, y0, 0) & 0xff; if (w != 0) { // Cycle on the selected Bands for (int i = 0; i < selectedBands; i++) { double sample = srcData[bands[i]][posx + posy + srcBandOffsets[bands[i]]]; // Control if the sample is Not a NO Data boolean isData = !noData.contains(sample); if (isData) { for (int j = 0; j < statNum; j++) { // Update of all the statistics statArray[i][j].addSample(sample); } } } } } } } } } } @Override public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { return destRect; } @Override public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { return sourceRect; } }