java image image-processing image-scaling

java - Escale una Imagen Buffered de la manera más rápida y fácil



resize image java (6)

El objeto Graphics tiene un método para dibujar una Image mientras se realiza una operación de cambio de tamaño:

Graphics.drawImage(Image, int, int, int, int, ImageObserver) método Graphics.drawImage(Image, int, int, int, int, ImageObserver) se puede usar para especificar la ubicación junto con el tamaño de la imagen al dibujar.

Entonces, podríamos usar un fragmento de código como este:

BufferedImage otherImage = // .. created somehow BufferedImage newImage = new BufferedImage(SMALL_SIZE, SMALL_SIZE, BufferedImage.TYPE_INT_RGB); Graphics g = newImage.createGraphics(); g.drawImage(otherImage, 0, 0, SMALL_SIZE, SMALL_SIZE, null); g.dispose();

Esto tomará otherImage y la dibujará en la newImage con el ancho y alto de SMALL_SIZE .

O, si no te importa usar una biblioteca, Thumbnailator podría lograr lo mismo con esto:

BufferedImage newImage = Thumbnails.of(otherImage) .size(SMALL_SIZE, SMALL_SIZE) .asBufferedImage();

Thumbnailator también realizará la operación de cambio de tamaño más rápido que el uso de Image.getScaledInstance y también realizará operaciones de cambio de tamaño de mayor calidad que utilizando solo Graphics.drawImage .

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

La tarea: tengo algunas imágenes, las bajó y las uní a una imagen. Pero tengo un pequeño problema con la implementación:

El problema concreto: quiero cambiar el tamaño / escalar una Imagen Buffered. El método getScaledInstance devuelve un objeto Image, pero no puedo convertirlo en BufferedImage:

Exception in thread "main" java.lang.ClassCastException: sun.awt.image.ToolkitImage cannot be cast to java.awt.image.BufferedImage

(No sé por qué es una ToolkitImage en lugar de una Imagen ...)

Encontré una solución:

Image tmp = bi.getScaledInstance(SMALL_SIZE, SMALL_SIZE, BufferedImage.SCALE_FAST); BufferedImage buffered = new BufferedImage(SMALL_SIZE,SMALL_SIZE,BufferedImage.TYPE_INT_RGB); buffered.getGraphics().drawImage(tmp, 0, 0, null);

Pero es lento, y creo que debería haber una mejor manera de hacerlo.

Necesito BufferedImage, porque tengo que obtener los píxeles para unir las imágenes pequeñas.

¿Hay una forma mejor (mejor / más rápida) de hacerlo?

EDITAR: si lanzo la imagen primero a ToolkitImage, tiene un método getBufferedImage (). Pero siempre devuelve nulo. ¿Sabes por qué?


Lo entiendo con este método, cambia el tamaño de la imagen e intenta mantener las proporciones:

/** * Resizes an image using a Graphics2D object backed by a BufferedImage. * @param srcImg - source image to scale * @param w - desired width * @param h - desired height * @return - the new resized image */ private BufferedImage getScaledImage(BufferedImage src, int w, int h){ int finalw = w; int finalh = h; double factor = 1.0d; if(src.getWidth() > src.getHeight()){ factor = ((double)src.getHeight()/(double)src.getWidth()); finalh = (int)(finalw * factor); }else{ factor = ((double)src.getWidth()/(double)src.getHeight()); finalw = (int)(finalh * factor); } BufferedImage resizedImg = new BufferedImage(finalw, finalh, BufferedImage.TRANSLUCENT); Graphics2D g2 = resizedImg.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(src, 0, 0, finalw, finalh, null); g2.dispose(); return resizedImg; }



Ninguna de estas respuestas fue lo suficientemente rápida para mí. Así que finalmente programé mi propio procedimiento.

static BufferedImage scale(BufferedImage src, int w, int h) { BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); int x, y; int ww = src.getWidth(); int hh = src.getHeight(); for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { int col = src.getRGB(x * ww / w, y * hh / h); img.setRGB(x, y, col); } } return img; }


Tal vez este método ayude:

public BufferedImage resizeImage(BufferedImage image, int width, int height) { int type=0; type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType(); BufferedImage resizedImage = new BufferedImage(width, height,type); Graphics2D g = resizedImage.createGraphics(); g.drawImage(image, 0, 0, width, height, null); g.dispose(); return resizedImage; }

No olvide esas líneas de "importación":

import java.awt.Graphics2D; import java.awt.image.BufferedImage;

Y sobre el casting:

La clase de la clase abstracta es la superclase de todas las clases que representan imágenes gráficas. No podemos convertir Image en BufferedImage porque cada imagen BufferedImage es una Image pero viceversa no es verdadera.

Image im = new BufferedImage(width, height, imageType);//this is true BufferedImage img = new Image(){//.....}; //this is wrong


También puede usar la biblioteca OpenCV Java. Su operación de cambio de tamaño es más rápida que la de Imgscalr:

Prueba

Image 5184 x 3456 escalado a 150 x 100 (esta es la versión más pequeña porque el archivo original es más grande que 2mb):

Imgscalr

Dependencia:

<dependency> <groupId>org.imgscalr</groupId> <artifactId>imgscalr-lib</artifactId> <version>4.2</version> </dependency>

Código:

BufferedImage thumbnail = Scalr.resize(img, Scalr.Method.SPEED, Scalr.Mode.AUTOMATIC, 150, 100);

Imagen del resultado:

Tiempo promedio: 80 millis

OpenCV

Dependencia:

<dependency> <groupId>nu.pattern</groupId> <artifactId>opencv</artifactId> <version>2.4.9-4</version> </dependency>

Convertir BufferedImage en Mat objeto (tener que):

BufferedImage img = ImageIO.read(image); // load image byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()) .getData(); Mat matImg = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3); matImg.put(0, 0, pixels);

Código:

Imgproc.resize(matImg, resizeimage, sz);

Configuración adicional (para windows):

Agregue opencv_java249.dll al directorio bin de su JDK.

Imagen del resultado:

Tiempo promedio: 13 millis

Resultados totales

En la prueba solo se calculan los tiempos de las funciones de "cambio de tamaño". Imgscalr redimensionó la imagen dada en 80 milis donde OpenCV realizó la misma tarea en 13 milisegundos. Puede encontrar todo el proyecto a continuación aquí para jugarlo un poco.

Como usted también preguntó de manera fácil, si el rendimiento de la biblioteca Imgscalr es bueno para usted, entonces es mortalmente fácil. Porque para usar OpenCV como ve, un archivo de biblioteca debe estar ubicado en todos sus entornos de desarrollo y servidores. También debes usar objetos Mat.

Todo el proyecto

Pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.btasdemir</groupId> <artifactId>testapp</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>testapp</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.imgscalr</groupId> <artifactId>imgscalr-lib</artifactId> <version>4.2</version> </dependency> <dependency> <groupId>nu.pattern</groupId> <artifactId>opencv</artifactId> <version>2.4.9-4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <version>0.9</version> </plugin> </plugins> </build> </project>

App.java:

package com.btasdemir.testapp; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import org.imgscalr.Scalr; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Size; import org.opencv.highgui.Highgui; import org.opencv.imgproc.Imgproc; /** * Hello world! * */ public class App { public static void main( String[] args ) throws IOException { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); File image = new File("C://your_dir//test.jpg"); BufferedImage img = ImageIO.read(image); // load image long startTime = System.currentTimeMillis();//imgscalr------------------------------------------------------ //resize to 150 pixels max BufferedImage thumbnail = Scalr.resize(img, Scalr.Method.SPEED, Scalr.Mode.AUTOMATIC, 150, 100); // BufferedImage thumbnail = Scalr.resize(img, // Scalr.Method.SPEED, // Scalr.Mode.AUTOMATIC, // 150, // 100, // Scalr.OP_ANTIALIAS); System.out.println(calculateElapsedTime(startTime));//END-imgscalr------------------------------------------------------ File outputfile = new File("C://your_dir//imgscalr_result.jpg"); ImageIO.write(thumbnail, "jpg", outputfile); img = ImageIO.read(image); // load image byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()) .getData(); Mat matImg = new Mat(img.getHeight(), img.getWidth(), CvType.CV_8UC3); matImg.put(0, 0, pixels); Mat resizeimage = new Mat(); Size sz = new Size(150, 100); startTime = System.currentTimeMillis();//opencv------------------------------------------------------ Imgproc.resize(matImg, resizeimage, sz); // Imgproc.resize(matImg, resizeimage, sz, 0.5, 0.5, Imgproc.INTER_CUBIC); System.out.println(calculateElapsedTime(startTime));//END-opencv------------------------------------------------------ Highgui.imwrite("C://your_dir//opencv_result.jpg", resizeimage); } protected static long calculateElapsedTime(long startTime) { long stopTime = System.currentTimeMillis(); long elapsedTime = stopTime - startTime; return elapsedTime; } }