icon change java image image-processing bufferedimage image-scaling

java - change - Cómo escalar una Imagen Buffered



resize imageicon java (7)

Como dice @Bozho, probablemente quieras usar getScaledInstance .

Sin embargo, para entender cómo funciona grph.scale(2.0, 2.0) , puedes echarle un vistazo a este código:

import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; import javax.swing.ImageIcon; class Main { public static void main(String[] args) throws IOException { final int SCALE = 2; Image img = new ImageIcon("duke.png").getImage(); BufferedImage bi = new BufferedImage(SCALE * img.getWidth(null), SCALE * img.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D grph = (Graphics2D) bi.getGraphics(); grph.scale(SCALE, SCALE); // everything drawn with grph from now on will get scaled. grph.drawImage(img, 0, 0, null); grph.dispose(); ImageIO.write(bi, "png", new File("duke_double_size.png")); } }

Dado duke.png :

produce duke_double_size.png :

Después de los javadocs, intenté escalar una BufferedImage sin éxito aquí está mi código:

BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded); Graphics2D grph = image.createGraphics(); grph.scale(2.0, 2.0); grph.dispose();

No puedo entender por qué no está funcionando, ¿alguna ayuda?


Lamentablemente, el rendimiento de getScaledInstance () es muy pobre, si no problemático.

El enfoque alternativo es crear una nueva Imagen Buffered y dibujar una versión escalada del original en la nueva.

BufferedImage resized = new BufferedImage(newWidth, newHeight, original.getType()); Graphics2D g = resized.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(original, 0, 0, newWidth, newHeight, 0, 0, original.getWidth(), original.getHeight(), null); g.dispose();

newWidth, newHeight indican el nuevo tamaño de BufferedImage y deben calcularse correctamente. En caso de escala de factor:

int newWidth = new Double(original.getWidth() * widthFactor).intValue(); int newHeight = new Double(original.getHeight() * heightFactor).intValue();

EDIT : encontré el artículo que ilustra el problema de rendimiento: The Perils of Image.getScaledInstance ()


Para escalar una imagen, necesita crear una nueva imagen y dibujar en ella. Una forma es usar el método filter() de AffineTransferOp , como se sugiere here . Esto le permite elegir la técnica de interpolación.

private static BufferedImage scale1(BufferedImage before, double scale) { int w = before.getWidth(); int h = before.getHeight(); // Create a new image of the proper size int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); scaleOp.filter(before, after); return after; }

Otra forma es simplemente dibujar la imagen original en la nueva imagen, usando una operación de escalado para escalar. Este método es muy similar, pero también ilustra cómo se puede dibujar lo que quieras en la imagen final. (Puse una línea en blanco donde los dos métodos comienzan a diferir).

private static BufferedImage scale2(BufferedImage before, double scale) { int w = before.getWidth(); int h = before.getHeight(); // Create a new image of the proper size int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); Graphics2D g2 = (Graphics2D) after.getGraphics(); // Here, you may draw anything you want into the new image, but we''re // drawing a scaled version of the original image. g2.drawImage(before, scaleOp, 0, 0); g2.dispose(); return after; }

Adición: Resultados

Para ilustrar las diferencias, comparé los resultados de los cinco métodos a continuación. Aquí se muestran los resultados, escalados tanto hacia arriba como hacia abajo, junto con los datos de rendimiento. (El rendimiento varía de una ejecución a la siguiente, así que tome estos números solo como pautas generales). La imagen superior es la original. Lo escalo de tamaño doble y de la mitad del tamaño.

Como puede ver, AffineTransformOp.filter() , utilizado en scaleBilinear() , es más rápido que el método de dibujo estándar de Graphics2D.drawImage() en scale2() . También la interpolación BiCubic es la más lenta, pero ofrece los mejores resultados al expandir la imagen. (Para el rendimiento, solo se debe comparar con scaleBilinear() y scaleNearest(). ) Bilinear parece ser mejor para reducir la imagen, aunque es una decisión difícil. Y NearestNeighbor es el más rápido, con los peores resultados. Bilinear parece ser el mejor compromiso entre velocidad y calidad. Image.getScaledInstance() , invocado en el método questionable() , tuvo un rendimiento muy bajo y devolvió la misma baja calidad que NearestNeighbor. (Los números de rendimiento solo se dan para expandir la imagen).

public static BufferedImage scaleBilinear(BufferedImage before, double scale) { final int interpolation = AffineTransformOp.TYPE_BILINEAR; return scale(before, scale, interpolation); } public static BufferedImage scaleBicubic(BufferedImage before, double scale) { final int interpolation = AffineTransformOp.TYPE_BICUBIC; return scale(before, scale, interpolation); } public static BufferedImage scaleNearest(BufferedImage before, double scale) { final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; return scale(before, scale, interpolation); } @NotNull private static BufferedImage scale(final BufferedImage before, final double scale, final int type) { int w = before.getWidth(); int h = before.getHeight(); int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, before.getType()); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type); scaleOp.filter(before, after); return after; } /** * This is a more generic solution. It produces the same result, but it shows how you * can draw anything you want into the newly created image. It''s slower * than scaleBilinear(). * @param before The original image * @param scale The scale factor * @return A scaled version of the original image */ private static BufferedImage scale2(BufferedImage before, double scale) { int w = before.getWidth(); int h = before.getHeight(); // Create a new image of the proper size int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, before.getType()); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); Graphics2D g2 = (Graphics2D) after.getGraphics(); // Here, you may draw anything you want into the new image, but we''re just drawing // a scaled version of the original image. This is slower than // calling scaleOp.filter(). g2.drawImage(before, scaleOp, 0, 0); g2.dispose(); return after; } /** * I call this one "questionable" because it uses the questionable getScaledImage() * method. This method is no longer favored because it''s slow, as my tests confirm. * @param before The original image * @param scale The scale factor * @return The scaled image. */ private static Image questionable(final BufferedImage before, double scale) { int w2 = (int) (before.getWidth() * scale); int h2 = (int) (before.getHeight() * scale); return before.getScaledInstance(w2, h2, Image.SCALE_FAST); }


Si no te importa usar una biblioteca externa, Thumbnailator puede realizar escalas de BufferedImage .

Thumbnailator se ocupará de manejar el procesamiento 2D de Java (como el uso de Graphics2D y el establecimiento de sugerencias de representación apropiadas) para que se pueda usar una simple llamada API fluida para cambiar el tamaño de las imágenes:

BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage();

Aunque Thumbnailator, como su nombre lo indica, está orientado a la reducción de imágenes, también hará un trabajo decente ampliando las imágenes, utilizando la interpolación bilineal en su implementación predeterminada de resizer.

Descargo de responsabilidad: soy el mantenedor de la biblioteca Thumbnailator .



AffineTransformOp ofrece la flexibilidad adicional de elegir el tipo de interpolación.

BufferedImage before = getBufferedImage(encoded); int w = before.getWidth(); int h = before.getHeight(); BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); AffineTransform at = new AffineTransform(); at.scale(2.0, 2.0); AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); after = scaleOp.filter(before, after);

El fragmento mostrado ilustra el resampling , no el cropping ; esta answer relacionada aborda el issue ; algunos ejemplos relacionados se examinan here .


scale(..) funciona de forma un poco diferente. Puedes usar bufferedImage.getScaledInstance(..)