/** * Creates a SampleOpImage with the given ParameterBlock if the SampleOpImage can handle the * particular ParameterBlock. */ @Override public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { PlanarImage source1 = (PlanarImage) paramBlock.getRenderedSource(0); if (source1 == null) { return null; } ROIShape shape = (ROIShape) paramBlock.getObjectParameter(0); if (shape == null) { return source1; } TiledImage image; if (ImageUtil.isBinary(source1.getSampleModel())) { image = new TiledImage( source1.getMinX(), source1.getMinY(), source1.getWidth(), source1.getHeight(), source1.getTileGridXOffset(), source1.getTileGridYOffset(), LayoutUtil.createBinarySampelModel(), LayoutUtil.createBinaryIndexColorModel()); } else { // rgb cannot be null or have less than one value Byte[] rgb = (Byte[]) paramBlock.getObjectParameter(1); int nbands = source1.getSampleModel().getNumBands(); if (rgb.length != nbands) { Byte fillVal = rgb[0]; rgb = new Byte[nbands]; Arrays.fill(rgb, fillVal); } image = ImageFiler.getEmptyTiledImage(rgb, source1.getWidth(), source1.getHeight()); } image.set(source1, shape); return image; }
/* * Create a Raster from a JPEG-compressed data stream. */ private Raster getJPEGTile(int tx, int ty, int subType, byte[] data) { int tableIndex = (subType >> 24) & 0x000000ff; ; boolean colorConversion = (subType & 0x00ff0000) != 0; JPEGDecodeParam decodeParam = null; if (tableIndex != 0) { decodeParam = getJPEGDecodeParam(tableIndex); } ByteArrayInputStream byteStream = new ByteArrayInputStream(data); JPEGImageDecoder decoder = decodeParam == null ? JPEGCodec.createJPEGDecoder(byteStream) : JPEGCodec.createJPEGDecoder(byteStream, decodeParam); Raster raster = null; try { raster = decoder.decodeAsRaster().createTranslatedChild(tx, ty); } catch (Exception e) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); listener.errorOccurred( JaiI18N.getString("IIPResolutionOpImage3"), new ImagingException(e), this, false); /* String msg = JaiI18N.getString("IIPResolutionOpImage3")+" "+ e.getMessage(); throw new RuntimeException(msg); */ } closeStream(byteStream); if (colorSpaceType == CS_NIFRGB && colorConversion) { YCbCrToNIFRGB(raster); } return raster; }
// This method is similar to the testBandMerge method but it tests the ExtendedBandMergeOpImage // class private void testExtendedBandMerge(RenderedImage[] sources, boolean noDataUsed, boolean roiUsed) { // Optional No Data Range used Range[] noData; // Source image data type int dataType = sources[0].getSampleModel().getDataType(); // If no Data are present, the No Data Range associated is used if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noData = noDataByte; break; case DataBuffer.TYPE_USHORT: noData = noDataUShort; break; case DataBuffer.TYPE_SHORT: noData = noDataShort; break; case DataBuffer.TYPE_INT: noData = noDataInt; break; case DataBuffer.TYPE_FLOAT: noData = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noData = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noData = null; } // ROI to use ROI roi = null; if (roiUsed) { roi = roiData; } // New array ofr the transformed source images RenderedOp[] translated = new RenderedOp[sources.length]; List<AffineTransform> transform = new ArrayList<AffineTransform>(); for (int i = 0; i < sources.length; i++) { // Translation coefficients int xTrans = (int) (Math.random() * 10); int yTrans = (int) (Math.random() * 10); // Translation operation AffineTransform tr = AffineTransform.getTranslateInstance(xTrans, yTrans); // Addition to the transformations list transform.add(tr); // Translation of the image translated[i] = TranslateDescriptor.create(sources[i], (float) xTrans, (float) yTrans, null, null); } // Definition of the final image dimensions ImageLayout layout = new ImageLayout(); layout.setMinX(sources[0].getMinX()); layout.setMinY(sources[0].getMinY()); layout.setWidth(sources[0].getWidth()); layout.setHeight(sources[0].getHeight()); RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); // BandMerge operation RenderedOp merged = BandMergeDescriptor.create(noData, destNoData, hints, transform, roi, translated); Assert.assertNotNull(merged.getTiles()); // Check if the bands number is the same assertEquals(BAND_NUMBER, merged.getNumBands()); // Upper-Left tile indexes int minTileX = merged.getMinTileX(); int minTileY = merged.getMinTileY(); // Raster object Raster upperLeftTile = merged.getTile(minTileX, minTileY); // Tile bounds int minX = upperLeftTile.getMinX(); int minY = upperLeftTile.getMinY(); int maxX = upperLeftTile.getWidth() + minX; int maxY = upperLeftTile.getHeight() + minY; // Source corners final int dstMinX = merged.getMinX(); final int dstMinY = merged.getMinY(); final int dstMaxX = merged.getMaxX(); final int dstMaxY = merged.getMaxY(); Point2D ptDst = new Point2D.Double(0, 0); Point2D ptSrc = new Point2D.Double(0, 0); // Cycle on all the tile Bands for (int b = 0; b < BAND_NUMBER; b++) { RandomIter iter = RandomIterFactory.create(translated[b], null); // Source corners final int srcMinX = translated[b].getMinX(); final int srcMinY = translated[b].getMinY(); final int srcMaxX = translated[b].getMaxX(); final int srcMaxY = translated[b].getMaxY(); // Cycle on the y-axis for (int x = minX; x < maxX; x++) { // Cycle on the x-axis for (int y = minY; y < maxY; y++) { // Calculated value double value = upperLeftTile.getSampleDouble(x, y, b); // If the tile pixels are outside the image bounds, then no data is set. if (x < dstMinX || x >= dstMaxX || y < dstMinY || y >= dstMaxY) { value = destNoData; } // Set the x,y destination pixel location ptDst.setLocation(x, y); // Map destination pixel to source pixel transform.get(b).transform(ptDst, ptSrc); // Source pixel indexes int srcX = round(ptSrc.getX()); int srcY = round(ptSrc.getY()); double valueOld = destNoData; // Check if the pixel is inside the source bounds if (!(srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY)) { // Old band value valueOld = iter.getSampleDouble(srcX, srcY, 0); } // ROI CHECK boolean contained = true; if (roiUsed) { if (!roi.contains(x, y)) { contained = false; // Comparison if the final value is not inside a ROI assertEquals(value, destNoData, TOLERANCE); } } if (contained) { // If no Data are present, no data check is performed if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: byte sampleB = ImageUtil.clampRoundByte(value); byte sampleBOld = ImageUtil.clampRoundByte(valueOld); if (noData[0].contains(sampleBOld)) { assertEquals(sampleB, destNoData, TOLERANCE); } else { assertEquals(sampleB, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_USHORT: short sampleUS = ImageUtil.clampRoundUShort(value); short sampleUSOld = ImageUtil.clampRoundUShort(valueOld); if (noData[0].contains(sampleUSOld)) { assertEquals(sampleUS, destNoData, TOLERANCE); } else { assertEquals(sampleUS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_SHORT: short sampleS = ImageUtil.clampRoundShort(value); short sampleSOld = ImageUtil.clampRoundShort(valueOld); if (noData[0].contains(sampleSOld)) { assertEquals(sampleS, destNoData, TOLERANCE); } else { assertEquals(sampleS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_INT: int sampleI = ImageUtil.clampRoundInt(value); int sampleIOld = ImageUtil.clampRoundInt(valueOld); if (noData[0].contains(sampleIOld)) { assertEquals(sampleI, destNoData, TOLERANCE); } else { assertEquals(sampleI, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_FLOAT: float sampleF = ImageUtil.clampFloat(value); float sampleFOld = ImageUtil.clampFloat(valueOld); if (noData[0].contains(sampleFOld)) { assertEquals(sampleF, destNoData, TOLERANCE); } else { assertEquals(sampleF, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_DOUBLE: if (noData[0].contains(valueOld)) { assertEquals(value, destNoData, TOLERANCE); } else { assertEquals(value, valueOld, TOLERANCE); } break; default: throw new IllegalArgumentException("Wrong data type"); } } else { // Else a simple value comparison is done assertEquals(value, valueOld, TOLERANCE); } } } } } // Disposal of the output image merged.dispose(); }
private void testBandMerge(RenderedImage[] sources, boolean noDataUsed, boolean roiUsed) { // Optional No Data Range used Range[] noData; // Source image data type int dataType = sources[0].getSampleModel().getDataType(); // If no Data are present, the No Data Range associated is used if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noData = noDataByte; break; case DataBuffer.TYPE_USHORT: noData = noDataUShort; break; case DataBuffer.TYPE_SHORT: noData = noDataShort; break; case DataBuffer.TYPE_INT: noData = noDataInt; break; case DataBuffer.TYPE_FLOAT: noData = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noData = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noData = null; } // ROI to use ROI roi = null; if (roiUsed) { roi = roiData; } // BandMerge operation RenderedOp merged = BandMergeDescriptor.create(noData, destNoData, null, null, roi, sources); // Check if the bands number is the same assertEquals(BAND_NUMBER, merged.getNumBands()); // Upper-Left tile indexes int minTileX = merged.getMinTileX(); int minTileY = merged.getMinTileY(); // Raster object Raster upperLeftTile = merged.getTile(minTileX, minTileY); // Tile bounds int minX = upperLeftTile.getMinX(); int minY = upperLeftTile.getMinY(); int maxX = upperLeftTile.getWidth() + minX; int maxY = upperLeftTile.getHeight() + minY; // Cycle on all the tile Bands for (int b = 0; b < BAND_NUMBER; b++) { // Selection of the source raster associated with the band Raster bandRaster = sources[b].getTile(minTileX, minTileY); // Cycle on the y-axis for (int x = minX; x < maxX; x++) { // Cycle on the x-axis for (int y = minY; y < maxY; y++) { // Calculated value double value = upperLeftTile.getSampleDouble(x, y, b); // Old band value double valueOld = bandRaster.getSampleDouble(x, y, 0); // ROI CHECK boolean contained = true; if (roiUsed) { if (!roi.contains(x, y)) { contained = false; // Comparison if the final value is not inside a ROI assertEquals(value, destNoData, TOLERANCE); } } if (contained) { // If no Data are present, no data check is performed if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: byte sampleB = ImageUtil.clampRoundByte(value); byte sampleBOld = ImageUtil.clampRoundByte(valueOld); if (noData[0].contains(sampleBOld)) { assertEquals(sampleB, destNoData, TOLERANCE); } else { assertEquals(sampleB, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_USHORT: short sampleUS = ImageUtil.clampRoundUShort(value); short sampleUSOld = ImageUtil.clampRoundUShort(valueOld); if (noData[0].contains(sampleUSOld)) { assertEquals(sampleUS, destNoData, TOLERANCE); } else { assertEquals(sampleUS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_SHORT: short sampleS = ImageUtil.clampRoundShort(value); short sampleSOld = ImageUtil.clampRoundShort(valueOld); if (noData[0].contains(sampleSOld)) { assertEquals(sampleS, destNoData, TOLERANCE); } else { assertEquals(sampleS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_INT: int sampleI = ImageUtil.clampRoundInt(value); int sampleIOld = ImageUtil.clampRoundInt(valueOld); if (noData[0].contains(sampleIOld)) { assertEquals(sampleI, destNoData, TOLERANCE); } else { assertEquals(sampleI, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_FLOAT: float sampleF = ImageUtil.clampFloat(value); float sampleFOld = ImageUtil.clampFloat(valueOld); if (noData[0].contains(sampleFOld)) { assertEquals(sampleF, destNoData, TOLERANCE); } else { assertEquals(sampleF, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_DOUBLE: if (noData[0].contains(valueOld)) { assertEquals(value, destNoData, TOLERANCE); } else { assertEquals(value, valueOld, TOLERANCE); } break; default: throw new IllegalArgumentException("Wrong data type"); } } else { // Else a simple value comparison is done assertEquals(value, valueOld, TOLERANCE); } } } } } // Disposal of the output image merged.dispose(); }
public void testRescale( RenderedImage source, boolean roiUsed, boolean noDataUsed, boolean useRoiAccessor, TestSelection select) { // The precalculated roi is used, if selected by the related boolean. ROI roiData; if (roiUsed) { roiData = roi; } else { roiData = null; } // The precalculated NoData Range is used, if selected by the related boolean. Range noDataRange; // Image data type int dataType = source.getSampleModel().getDataType(); if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noDataRange = noDataByte; break; case DataBuffer.TYPE_USHORT: noDataRange = noDataUShort; break; case DataBuffer.TYPE_SHORT: noDataRange = noDataShort; break; case DataBuffer.TYPE_INT: noDataRange = noDataInt; break; case DataBuffer.TYPE_FLOAT: noDataRange = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noDataRange = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noDataRange = null; } // Rescale operation PlanarImage rescaled = RescaleDescriptor.create( source, scales, offsets, roiData, noDataRange, useRoiAccessor, destNoData, null); // Display Image if (INTERACTIVE && TEST_SELECTOR == select.getType()) { RenderedImageBrowser.showChain(rescaled, false, roiUsed); try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } } else { // Calculation of all the image tiles rescaled.getTiles(); } // Rescale control on the first band int tileMinX = rescaled.getMinTileX(); int tileMinY = rescaled.getMinTileY(); // Selection of the source and destination first tile Raster tileDest = rescaled.getTile(tileMinX, tileMinY); Raster tileSource = source.getTile(tileMinX, tileMinY); int tileMinXpix = tileDest.getMinX(); int tileMinYpix = tileDest.getMinY(); int tileMaxXpix = tileDest.getWidth() + tileMinXpix; int tileMaxYpix = tileDest.getHeight() + tileMinYpix; double scaleFactor = scales[0]; double offset = offsets[0]; // loop through the tile pixels for (int i = tileMinXpix; i < tileMaxXpix; i++) { for (int j = tileMinYpix; j < tileMaxYpix; j++) { switch (dataType) { case DataBuffer.TYPE_BYTE: // selection of the rescaled pixel byte destValueB = (byte) tileDest.getSample(i, j, 0); // rescale operation on the source pixel int srcValueB = tileSource.getSample(i, j, 0) & 0xFF; byte calculationB = ImageUtil.clampRoundByte(srcValueB * scaleFactor + offset); // comparison if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains((byte) srcValueB)) { assertEquals(calculationB, destValueB); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationB, destValueB); } } else if (noDataUsed) { if (!noDataRange.contains((byte) srcValueB)) { assertEquals(calculationB, destValueB); } } else { assertEquals(calculationB, destValueB); } break; case DataBuffer.TYPE_USHORT: short destValueU = (short) tileDest.getSample(i, j, 0); int srcValueU = tileSource.getSample(i, j, 0) & 0xFFFF; short calculationU = ImageUtil.clampRoundUShort(srcValueU * scaleFactor + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains((short) srcValueU)) { assertEquals(calculationU, destValueU); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationU, destValueU); } } else if (noDataUsed) { if (!noDataRange.contains((short) srcValueU)) { assertEquals(calculationU, destValueU); } } else { assertEquals(calculationU, destValueU); } break; case DataBuffer.TYPE_SHORT: short destValueS = (short) tileDest.getSample(i, j, 0); short srcValueS = (short) tileSource.getSample(i, j, 0); short calculationS = ImageUtil.clampRoundShort(srcValueS * scaleFactor + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueS)) { assertEquals(calculationS, destValueS); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationS, destValueS); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueS)) { assertEquals(calculationS, destValueS); } } else { assertEquals(calculationS, destValueS); } break; case DataBuffer.TYPE_INT: int destValueI = tileDest.getSample(i, j, 0); int srcValueI = tileSource.getSample(i, j, 0); int calculationI = ImageUtil.clampRoundInt(srcValueI * scaleFactor + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueI)) { assertEquals(calculationI, destValueI); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationI, destValueI); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueI)) { assertEquals(calculationI, destValueI); } } else { assertEquals(calculationI, destValueI); } break; case DataBuffer.TYPE_FLOAT: float destValueF = tileDest.getSampleFloat(i, j, 0); float srcValueF = tileSource.getSampleFloat(i, j, 0); float calculationF = (float) ((srcValueF * scaleFactor) + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueF)) { assertEquals(calculationF, destValueF, TOLERANCE); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationF, destValueF, TOLERANCE); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueF)) { assertEquals(calculationF, destValueF, TOLERANCE); } } else { assertEquals(calculationF, destValueF, TOLERANCE); } break; case DataBuffer.TYPE_DOUBLE: double destValueD = tileDest.getSampleDouble(i, j, 0); double srcValueD = tileSource.getSampleDouble(i, j, 0); double calculationD = ((srcValueD * scaleFactor) + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueD)) { assertEquals(calculationD, destValueD, TOLERANCE); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationD, destValueD, TOLERANCE); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueD)) { assertEquals(calculationD, destValueD, TOLERANCE); } } else { assertEquals(calculationD, destValueD, TOLERANCE); } break; default: throw new IllegalArgumentException("Wrong data type"); } } } }
private void testType(RenderedImage src, boolean nodataUsed, boolean roiUsed) { // Optional No Data Range used Range noData; // Source image data type int dataType = src.getSampleModel().getDataType(); // If no Data are present, the No Data Range associated is used if (nodataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noData = noDataByte; break; case DataBuffer.TYPE_USHORT: noData = noDataUShort; break; case DataBuffer.TYPE_SHORT: noData = noDataShort; break; case DataBuffer.TYPE_INT: noData = noDataInt; break; case DataBuffer.TYPE_FLOAT: noData = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noData = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noData = null; } ROI roi; if (roiUsed) { roi = roiObject; } else { roi = null; } // BandCombined result RenderedOp combined = BandCombineDescriptor.create(src, matrix, roi, noData, destinationNoData, null); int tileWidth = combined.getTileWidth(); int tileHeight = combined.getTileHeight(); int minTileX = combined.getMinTileX(); int minTileY = combined.getMinTileY(); int numXTiles = combined.getNumXTiles(); int numYTiles = combined.getNumYTiles(); int maxTileX = minTileX + numXTiles; int maxTileY = minTileY + numYTiles; // Ensure same size assertEquals(combined.getWidth(), src.getWidth()); assertEquals(combined.getHeight(), src.getHeight()); assertEquals(combined.getMinX(), src.getMinX()); assertEquals(combined.getMinY(), src.getMinY()); assertEquals(minTileX, src.getMinTileX()); assertEquals(minTileY, src.getMinTileY()); assertEquals(numXTiles, src.getNumXTiles()); assertEquals(numYTiles, src.getNumYTiles()); assertEquals(tileWidth, src.getTileWidth()); assertEquals(tileHeight, src.getTileHeight()); int srcBands = src.getSampleModel().getNumBands(); int dstBands = combined.getNumBands(); // Ensure a correct band size assertEquals(dstBands, matrix.length); // Check on all the pixels if they have been calculate correctly for (int tileX = minTileX; tileX < maxTileX; tileX++) { for (int tileY = minTileY; tileY < maxTileY; tileY++) { Raster tile = combined.getTile(tileX, tileY); Raster srcTile = src.getTile(tileX, tileY); int minX = tile.getMinX(); int minY = tile.getMinY(); int maxX = minX + tileWidth - 1; int maxY = minY + tileHeight - 1; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { boolean isValidRoi = !roiUsed || (roiUsed && roiObject.contains(x, y)); if (isValidRoi) { for (int b = 0; b < dstBands; b++) { // Getting the result double result = tile.getSampleDouble(x, y, b); // Calculating the expected result from sources boolean valid = false; double calculated = 0; for (int i = 0; i < srcBands; i++) { double sample = srcTile.getSampleDouble(x, y, i); boolean isValidData = !nodataUsed || (nodataUsed && !noDataDouble.contains(sample)); valid |= isValidData; if (isValidData) { switch (dataType) { case DataBuffer.TYPE_BYTE: calculated += ((int) sample & 0xFF) * matrix[b][i]; break; case DataBuffer.TYPE_USHORT: calculated += ((int) sample & 0xFFFF) * matrix[b][i]; break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: calculated += sample * matrix[b][i]; break; default: break; } } } if (valid) { calculated += matrix[b][srcBands]; switch (dataType) { case DataBuffer.TYPE_BYTE: calculated = ImageUtil.clampRoundByte(calculated); result = ImageUtil.clampRoundByte(result); break; case DataBuffer.TYPE_USHORT: calculated = ImageUtil.clampRoundUShort(calculated); result = ImageUtil.clampRoundUShort(result); break; case DataBuffer.TYPE_SHORT: calculated = ImageUtil.clampRoundShort(calculated); result = ImageUtil.clampRoundShort(result); break; case DataBuffer.TYPE_INT: calculated = ImageUtil.clampRoundInt(calculated); result = ImageUtil.clampRoundInt(result); break; case DataBuffer.TYPE_FLOAT: calculated = (float) calculated; calculated = (float) result; break; case DataBuffer.TYPE_DOUBLE: break; default: break; } assertEquals(result, calculated, TOLERANCE); } else { assertEquals(result, destNoData, TOLERANCE); } } } else { for (int b = 0; b < dstBands; b++) { assertEquals(tile.getSampleDouble(x, y, b), destNoData, TOLERANCE); } } } } } } // Disposal of the output image combined.dispose(); }