/* 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.bandcombine; import it.geosolutions.jaiext.iterators.RandomIterFactory; import it.geosolutions.jaiext.range.Range; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Arrays; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFactory; import javax.media.jai.RasterFormatTag; import javax.media.jai.iterator.RandomIter; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An <code>OpImage</code> implementing the "BandCombine" operation taking into account the presence of ROI and NoData. * * <p> * This <code>OpImage</code> performs the arbitrary interband linear combination of an image using the specified matrix. The width of the matrix must * be one larger that the number of bands in the source image. The height of the matrix must be equal to the number of bands in the destination image. * Because the matrix can be of arbitrary size, this function can be used to produce a destination image with a different number of bands from the * source image. * <p> * The destination image is formed by performing a matrix- multiply operation between the bands of the source image and the specified matrix. The * extra column of values is a constant that is added after the matrix-multiply operation takes place. * * If an input sample is outside ROI or it is a NoData, it will be skipped during computation. * */ public class BandCombineOpImage extends PointOpImage { /** Constant indicating that the inner random iterators must pre-calculate an array of the image positions */ public static final boolean ARRAY_CALC = true; /** Constant indicating that the inner random iterators must cache the current tile position */ public static final boolean TILE_CACHED = true; /** Boolean indicating that NoData must be checked */ private final boolean hasNoData; /** NoData Range element */ private Range noData; /** LookupTable used for checking if an input byte sample is a NoData */ private boolean[] lut; /** Boolean indicating that ROI must be checked */ private final boolean hasROI; /** ROI element */ private ROI roi; /** Boolean indicating that no roi and no data check must be done */ private final boolean caseA; /** Boolean indicating that only roi check must be done */ private final boolean caseB; /** Boolean indicating that only no data check must be done */ private final boolean caseC; /** ROI bounds as a Shape */ private final Rectangle roiBounds; /** ROI related image */ private PlanarImage roiImage; /** Matrix used for doing band combination */ private double[][] matrix; /** Destination No Data value for Byte sources */ private byte destNoDataByte; /** Destination No Data value for Short sources */ private short destNoDataShort; /** Destination No Data value for Integer sources */ private int destNoDataInt; /** Destination No Data value for Float sources */ private float destNoDataFloat; /** Destination No Data value for Double sources */ private double destNoDataDouble; /** * Constructs a new instance of the {@link BandCombineOpImage}. * * @param source The source image. * @param layout The destination image layout. * @param matrix The matrix of values used to perform the linear combination. * @param roi ROI object * @param destinationNoData * @param nodata No Data Range used for checking if a pixel is a NoData. * @param destinationNoData value for replacing the source nodata values */ public BandCombineOpImage(RenderedImage source, Map config, ImageLayout layout, double[][] matrix, ROI roi, Range noData, double destinationNoData) { super(source, layout, config, true); // Setting matrix this.matrix = matrix; int numBands = matrix.length; // matrix height is dst numBands if (getSampleModel().getNumBands() != numBands) { sampleModel = RasterFactory.createComponentSampleModel(sampleModel, sampleModel.getDataType(), tileWidth, tileHeight, numBands); if (colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Check if ROI control must be done if (roi != null) { hasROI = true; // Roi object this.roi = roi; roiBounds = roi.getBounds(); } else { hasROI = false; this.roi = null; roiBounds = null; } // Check if No Data control must be done if (noData != null) { hasNoData = true; this.noData = noData; } else { hasNoData = false; } // Getting datatype int dataType = source.getSampleModel().getDataType(); // Destination No Data value is clamped to the image data type this.destNoDataDouble = destinationNoData; switch (dataType) { case DataBuffer.TYPE_BYTE: this.destNoDataByte = ImageUtil.clampRoundByte(destinationNoData); break; case DataBuffer.TYPE_USHORT: this.destNoDataShort = ImageUtil.clampRoundUShort(destinationNoData); break; case DataBuffer.TYPE_SHORT: this.destNoDataShort = ImageUtil.clampRoundShort(destinationNoData); break; case DataBuffer.TYPE_INT: this.destNoDataInt = ImageUtil.clampRoundInt(destinationNoData); break; case DataBuffer.TYPE_FLOAT: this.destNoDataFloat = ImageUtil.clampFloat(destinationNoData); break; case DataBuffer.TYPE_DOUBLE: break; default: throw new IllegalArgumentException("Wrong image data type"); } // 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; if (hasNoData && dataType == DataBuffer.TYPE_BYTE) { initBooleanNoDataTable(); } } /** * Performs linear combination of source image with matrix. If ROI and NoData are present they will be checked. * * @param sources Cobbled sources, guaranteed to provide all the source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor s = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // ROI fields ROI roiTile = null; RandomIter roiIter = null; boolean roiContainsTile = false; boolean roiDisjointTile = false; // ROI check if (hasROI) { Rectangle srcRectExpanded = mapDestRect(destRect, 0); // The tile dimension is extended for avoiding border errors srcRectExpanded.setRect(srcRectExpanded.getMinX() - 1, srcRectExpanded.getMinY() - 1, srcRectExpanded.getWidth() + 2, srcRectExpanded.getHeight() + 2); roiTile = roi.intersect(new ROIShape(srcRectExpanded)); // Check if the Tile bounds intersects the roi otherwise, the computation is skipped if (!roiBounds.intersects(srcRectExpanded)) { roiDisjointTile = true; } else { roiContainsTile = roiTile.contains(srcRectExpanded); if (!roiContainsTile) { if (!roiTile.intersects(srcRectExpanded)) { roiDisjointTile = true; } else { PlanarImage roiIMG = getImage(); roiIter = RandomIterFactory.create(roiIMG, null, TILE_CACHED, ARRAY_CALC); } } } } if (!hasROI || !roiDisjointTile) { switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s, d, roiIter, roiContainsTile); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s, d, roiIter, roiContainsTile); break; case DataBuffer.TYPE_SHORT: computeRectShort(s, d, roiIter, roiContainsTile); break; case DataBuffer.TYPE_INT: computeRectInt(s, d, roiIter, roiContainsTile); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s, d, roiIter, roiContainsTile); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s, d, roiIter, roiContainsTile); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } else { // Setting all as NoData double[] backgroundValues = new double[s.getNumBands()]; Arrays.fill(backgroundValues, destNoDataDouble); ImageUtil.fillBackground(dest, destRect, backgroundValues); } } private void computeRectByte(RasterAccessor s, RasterAccessor d, RandomIter roiIter, boolean roiContainsTile) { // Input parameters int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int srcBands = s.getNumBands(); int[] srcBandOffsets = s.getBandOffsets(); byte[][] srcData = s.getByteDataArrays(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstBands = d.getNumBands(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); byte[][] dstData = d.getByteDataArrays(); int srcLineOffset = 0; int dstLineOffset = 0; int x0 = 0; int y0 = 0; int srcX = s.getX(); int srcY = s.getY(); if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]] & 0xFF); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundByte(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseB) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataByte; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]] & 0xFF); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundByte(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { byte sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (lut[sample & 0xFF]) { valid = true; sum += (float) mat[k] * (float) (sample & 0xFF); } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundByte(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataByte; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataByte; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { byte sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (lut[sample & 0xFF]) { valid = true; sum += (float) mat[k] * (float) (sample & 0xFF); } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundByte(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataByte; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } } private void computeRectUShort(RasterAccessor s, RasterAccessor d, RandomIter roiIter, boolean roiContainsTile) { // Input parameters int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int srcBands = s.getNumBands(); int[] srcBandOffsets = s.getBandOffsets(); short[][] srcData = s.getShortDataArrays(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstBands = d.getNumBands(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); short[][] dstData = d.getShortDataArrays(); int srcLineOffset = 0; int dstLineOffset = 0; int x0 = 0; int y0 = 0; int srcX = s.getX(); int srcY = s.getY(); if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]] & 0xFFFF); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundUShort(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseB) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]] & 0xFFFF); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundUShort(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { short sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) (sample & 0xFFFF); } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundUShort(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { short sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) (sample & 0xFFFF); } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundUShort(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } } private void computeRectShort(RasterAccessor s, RasterAccessor d, RandomIter roiIter, boolean roiContainsTile) { // Input parameters int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int srcBands = s.getNumBands(); int[] srcBandOffsets = s.getBandOffsets(); short[][] srcData = s.getShortDataArrays(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstBands = d.getNumBands(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); short[][] dstData = d.getShortDataArrays(); int srcLineOffset = 0; int dstLineOffset = 0; int x0 = 0; int y0 = 0; int srcX = s.getX(); int srcY = s.getY(); if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundShort(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseB) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundShort(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { short sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundShort(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { short sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundShort(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataShort; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } } private void computeRectInt(RasterAccessor s, RasterAccessor d, RandomIter roiIter, boolean roiContainsTile) { // Input parameters int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int srcBands = s.getNumBands(); int[] srcBandOffsets = s.getBandOffsets(); int[][] srcData = s.getIntDataArrays(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstBands = d.getNumBands(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); int[][] dstData = d.getIntDataArrays(); int srcLineOffset = 0; int dstLineOffset = 0; int x0 = 0; int y0 = 0; int srcX = s.getX(); int srcY = s.getY(); if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundInt(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseB) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataInt; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundInt(sum); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { int sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundInt(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataInt; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataInt; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { int sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = ImageUtil .clampRoundInt(sum); } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataInt; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } } private void computeRectFloat(RasterAccessor s, RasterAccessor d, RandomIter roiIter, boolean roiContainsTile) { // Input parameters int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int srcBands = s.getNumBands(); int[] srcBandOffsets = s.getBandOffsets(); float[][] srcData = s.getFloatDataArrays(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstBands = d.getNumBands(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); float[][] dstData = d.getFloatDataArrays(); int srcLineOffset = 0; int dstLineOffset = 0; int x0 = 0; int y0 = 0; int srcX = s.getX(); int srcY = s.getY(); if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseB) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataFloat; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += (float) mat[k] * (float) (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { float sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataFloat; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataFloat; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization float sum = 0.0F; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { float sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataFloat; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } } private void computeRectDouble(RasterAccessor s, RasterAccessor d, RandomIter roiIter, boolean roiContainsTile) { // Input parameters int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int srcBands = s.getNumBands(); int[] srcBandOffsets = s.getBandOffsets(); double[][] srcData = s.getDoubleDataArrays(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstBands = d.getNumBands(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); double[][] dstData = d.getDoubleDataArrays(); int srcLineOffset = 0; int dstLineOffset = 0; int x0 = 0; int y0 = 0; int srcX = s.getX(); int srcY = s.getY(); if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization double sum = 0.0D; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += mat[k] * (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseB) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataDouble; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization double sum = 0.0D; // Coeffs for the Band b double[] mat = matrix[b]; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { sum += mat[k] * (srcData[k][srcPixelOffset + srcBandOffsets[k]]); } sum += (float) mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstBands; b++) { // Result initialization double sum = 0.0D; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { double sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += mat[k] * sample; } } if (valid) { sum += mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataDouble; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } else { for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // ROI check x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { // Setting destination NoData for (int b = 0; b < dstBands; b++) { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataDouble; } // Updating offset srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; continue; } for (int b = 0; b < dstBands; b++) { // Result initialization double sum = 0.0D; // Coeffs for the Band b double[] mat = matrix[b]; // Boolean variable indicating that all the samples are nodata boolean valid = false; // Cycle on the src bands and calculation of the combination for (int k = 0; k < srcBands; k++) { double sample = srcData[k][srcPixelOffset + srcBandOffsets[k]]; if (!noData.contains(sample)) { valid = true; sum += (float) mat[k] * (float) sample; } } if (valid) { sum += mat[srcBands]; dstData[b][dstPixelOffset + dstBandOffsets[b]] = sum; } else { dstData[b][dstPixelOffset + dstBandOffsets[b]] = destNoDataDouble; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; } } } /** * Private method used for generating the Boolean Lookup table used for checking if a Byte data is a NoData */ private void initBooleanNoDataTable() { // Initialization of the boolean lookup table lut = new boolean[256]; // Fill the lookuptable for (int i = 0; i < 256; i++) { boolean result = true; if (noData.contains((byte) i)) { result = false; } lut[i] = result; } } /** * This method provides a lazy initialization of the image associated to the ROI. The method uses the Double-checked locking in order to maintain * thread-safety * * @return */ private PlanarImage getImage() { PlanarImage img = roiImage; if (img == null) { synchronized (this) { img = roiImage; if (img == null) { roiImage = img = roi.getAsImage(); } } } return img; } }