/** * Scale an image up/down to the desired width and height, while maintaining the image's aspect * ratio (if requested). * * @param image the source image to scale * @param scaleWidth the new width to scale to * @param scaleHeight the new height to scale to * @param keepAspect true if the aspect ratio should be maintained. * @param color the color to fill borders when maintaining aspect ratio (0 = black, 255 = white). * @return a scaled image as RenderedOp * @see #scaleImage(RenderedOp, int, int) */ public static RenderedOp scaleImage( RenderedOp image, int scaleWidth, int scaleHeight, boolean keepAspect, double color) { float xScale = (float) scaleWidth / (float) image.getWidth(); float yScale = (float) scaleHeight / (float) image.getHeight(); boolean resize = false; if (keepAspect) { resize = Math.abs(xScale - yScale) < .0000001; xScale = Math.min(xScale, yScale); yScale = xScale; } ParameterBlock params = new ParameterBlock(); params.addSource(image); params.add(xScale); // x scale factor params.add(yScale); // y scale factor params.add(0.0F); // x translate params.add(0.0F); // y translate params.add(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); RenderedOp result = JAI.create("scale", params, ImageToolkit.NOCACHE_HINT); // $NON-NLS-1$ if (resize) { result = resizeImage(result, scaleWidth, scaleHeight, color); } return result; }
private RenderedOp createScaledImage( RenderedImage sourceImage, S2Resolution resolution, int level) { int sourceWidth = sourceImage.getWidth(); int sourceHeight = sourceImage.getHeight(); int targetWidth = imageLayouts[0].width >> level; int targetHeight = imageLayouts[0].height >> level; float scaleX = (float) targetWidth / (float) sourceWidth; float scaleY = (float) targetHeight / (float) sourceHeight; float corrFactorX = resolution == S2Resolution.R20M ? R20M_X_FACTOR : R60M_X_FACTOR; float corrFactorY = resolution == S2Resolution.R20M ? R20M_Y_FACTOR : R60M_Y_FACTOR; final Dimension tileDim = getTileDim(targetWidth, targetHeight); ImageLayout imageLayout = new ImageLayout(); imageLayout.setTileWidth(tileDim.width); imageLayout.setTileHeight(tileDim.height); RenderingHints renderingHints = new RenderingHints( JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_ZERO)); renderingHints.put(JAI.KEY_IMAGE_LAYOUT, imageLayout); RenderedOp scaledImage = ScaleDescriptor.create( sourceImage, scaleX * corrFactorX, scaleY * corrFactorY, 0F, 0F, Interpolation.getInstance(Interpolation.INTERP_NEAREST), renderingHints); if (scaledImage.getWidth() != targetWidth || scaledImage.getHeight() != targetHeight) { return CropDescriptor.create( scaledImage, 0.0F, 0.0F, (float) targetWidth, (float) targetHeight, null); } else { return scaledImage; } }
protected RenderedOp resizeJAI( int thumbWidth, int thumbHeight, RenderedOp image, boolean scaleComputed) { double scale = 1.0; if (!scaleComputed) { int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); scale = computeEvenResizeScale(thumbWidth, thumbHeight, imageWidth, imageHeight); } RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); return JAI.create("SubsampleAverage", image, scale, scale, qualityHints); }
/** * Crop down an image to smaller dimensions. Used by resizeImage() when an image dimension is * smaller. * * @param image the source image to crop * @param toWidth the new width to crop to * @param toHeight the new height to crop to * @return a cropped image as RenderedOp * @see #resizeImage(RenderedOp, int, int) */ public static RenderedOp cropImage(RenderedOp image, int toWidth, int toHeight) { int width = image.getWidth(); int height = image.getHeight(); int xOffset = (width - toWidth) / 2; int yOffset = (height - toHeight) / 2; ParameterBlock params = new ParameterBlock(); params.addSource(image); params.add((float) xOffset); // x origin params.add((float) yOffset); // y origin params.add((float) toWidth); // width params.add((float) toHeight); // height return JAI.create("crop", params, null); // $NON-NLS-1$ }
/** * Resize an image to the new dimensions - no scaling is performed on the image, but the canvas * size is changed. Any empty areas are filled with white. * * @param image the source image to resize * @param toWidth the new width to resize to * @param toHeight the new height to resize to * @param color the color to fill borders when resizing up. * @return the resized image as RenderedOp */ public static RenderedOp resizeImage(RenderedOp image, int toWidth, int toHeight, double color) { int width = image.getWidth(); int height = image.getHeight(); if (width > toWidth || height > toHeight) { image = cropImage(image, Math.min(width, toWidth), Math.min(height, toHeight)); } if (width < toWidth || height < toHeight) { int w = Math.max((toWidth - width) / 2, 0); int h = Math.max((toHeight - height) / 2, 0); image = borderImage(image, w, w, h, h, color); } return image; }
/** * This method downscales a band by a given factor * * @param inputBand - the input band * @param scalingFactor - the scaling factor * @return Band - the downscaled band */ public static Band downscaleBand(Band inputBand, float scalingFactor) { final RenderedImage sourceImage = inputBand.getSourceImage(); final RenderedOp downscaledImage = ScaleDescriptor.create( sourceImage, 1.0f / scalingFactor, 1.0f / scalingFactor, 0.0f, 0.0f, Interpolation.getInstance(Interpolation.INTERP_NEAREST), null); Band downscaledBand = new Band( inputBand.getName(), inputBand.getDataType(), downscaledImage.getWidth(), downscaledImage.getHeight()); downscaledBand.setSourceImage(downscaledImage); return downscaledBand; }
private static BufferedImage convolveICOL( RenderedOp source, KernelJAI gaussKernel, KernelJAI distKernel) { final int m = gaussKernel.getWidth(); final int n = m / 2 + 1; float[] weights = new float[n]; float wSum = 0; for (int k = 0; k < n; k++) { weights[k] = gaussKernel.getElement(distKernel.getXOrigin() + k, distKernel.getYOrigin()); wSum += weights[k]; } for (int k = 0; k < n; k++) { weights[k] /= wSum; } dumpArray("Weights", weights); int width = source.getWidth(); int height = source.getHeight(); Raster aRaster = source.getData(new Rectangle(0, 0, width, height)); BufferedImage cImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); byte[] aData = ((DataBufferByte) aRaster.getDataBuffer()).getData(); byte[] cData = ((DataBufferByte) cImage.getRaster().getDataBuffer()).getData(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float[] sums = new float[n]; int[] counts = new int[n]; for (int j = 0; j < m; j++) { for (int i = 0; i < m; i++) { int k = (int) distKernel.getElement(i, j); if (k > 0) { int xx = x + i - distKernel.getXOrigin(); int yy = y + j - distKernel.getYOrigin(); if (xx >= 0 && xx < width && yy >= 0 && yy < height) { int a = (aData[yy * width + xx] & 0xff); if (a > 0) { // = if (not no-data) sums[k] += a; counts[k]++; } } } } } int a = aData[y * width + x] & 0xff; float c = weights[0] * a; for (int k = 1; k < n; k++) { if (counts[k] > 0) { c += (weights[k] * sums[k]) / counts[k]; } } if (a == 0) { cData[y * width + x] = (byte) c; } else { cData[y * width + x] = aData[y * width + x]; } } } return cImage; }
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(); }