una tamaño perder para jpg imprimir imagen como cambiar calidad ajustar java image-processing

java - perder - Cómo mejorar el rendimiento del método g.drawImage() para cambiar el tamaño de las imágenes



como cambiar el tamaño de una imagen para imprimir (12)

Tengo una aplicación donde los usuarios pueden cargar imágenes en álbumes pero, naturalmente, las imágenes cargadas deben redimensionarse para que también haya disponibles pulgares y las imágenes mostradas también caben en la página (por ejemplo, 800x600). La forma en que hago el cambio de tamaño es así:

Image scaledImage = img.getScaledInstance((int)width, (int)height, Image.SCALE_SMOOTH); BufferedImage imageBuff = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_RGB); Graphics g = imageBuff.createGraphics(); g.drawImage(scaledImage, 0, 0, new Color(0,0,0), null); g.dispose();

Y funciona bien. Mi único problema es que el método g.drawImage() parece ser muy lento, y no puedo imaginar al usuario lo suficientemente paciente como para esperar una carga de 20 imágenes de 20 * 10 segundos ~ 3 minutos. De hecho, en mi computadora tardan casi 40 segundos en hacer los 3 tamaños diferentes para una sola imagen.

Eso no es lo suficientemente bueno, y estoy buscando una solución más rápida. Me pregunto si alguien podría decirme acerca de uno mejor en Java O llamando a un script de shell, comando, lo que sea que hackeo, tiene que ser más rápido, todo lo demás no importa esta vez.


¿Realmente necesitas la calidad que se proporciona al usar Image.SCALE_SMOOTH? Si no lo hace, puede intentar usar Image.SCALE_FAST . Puede encontrar este article útil si desea seguir con algo proporcionado por Java.


Alguna vez tendrá una compensación entre la velocidad del cambio de tamaño y la calidad de la imagen resultante. Puede probar otro algoritmo de escalado del JDK.

La mejor y más flexible herramienta para editar imágenes AFAIK es ImageMagick .

Hay dos interfaces para el lenguaje Java:

  • JMagick : es una interfaz JNI para ImageMagick. Vea los proyectos Wiki para obtener más información.
  • im4java - es una interfaz de línea de comandos para ImageMagick. No es, como JMagick, basado en JNI.

Debería preferir im4java antes de usar la línea de comando directamente para llamar a ImageMagick.


Algunas mejoras en el rendimiento (tal vez pequeñas, tal vez insignificantes, tal vez a expensas de la calidad) se pueden lograr ajustando las sugerencias de renderizado. P.ej

g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);


Bueno, Jacob y yo queríamos redimensionar una Imagen, no una Imagen Buffered. Así que terminamos con este código:

/** * we want the x and o to be resized when the JFrame is resized * * @param originalImage an x or an o. Use cross or oh fields. * * @param biggerWidth * @param biggerHeight */ private Image resizeToBig(Image originalImage, int biggerWidth, int biggerHeight) { int type = BufferedImage.TYPE_INT_ARGB; BufferedImage resizedImage = new BufferedImage(biggerWidth, biggerHeight, type); Graphics2D g = resizedImage.createGraphics(); g.setComposite(AlphaComposite.Src); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.drawImage(originalImage, 0, 0, biggerWidth, biggerHeight, this); g.dispose(); return resizedImage; }


El punto principal de la pregunta era sobre el rendimiento de escalar imágenes en Java . Las otras respuestas mostraron diferentes enfoques, sin evaluarlos más. Tenía curiosidad por esto también, así que traté de escribir una pequeña prueba de rendimiento. Sin embargo, es difícil probar el rendimiento de escala de la imagen de manera confiable , sensata y objetiva . Hay demasiados factores de influencia que deben tenerse en cuenta:

  • El tamaño de la imagen de entrada
  • El tamaño de la imagen de salida
  • La interpolación (es decir, "calidad": vecino más cercano, bilineal, bicúbico)
  • The BufferedImage.TYPE_* de la imagen de entrada
  • BufferedImage.TYPE_* de la imagen de salida
  • La versión JVM y el sistema operativo
  • Finalmente: el método que realmente se usa para realizar la operación.

Intenté cubrir aquellos que consideraba los más importantes. La configuración fue:

  • La entrada es una foto simple, "promedio" (en particular, esta "Imagen del día" de Wikipedia, con un tamaño de 2560x1706 píxeles)

  • Los principales tipos de interpolación se prueban, es decir, usando RenderingHints donde la tecla INTERPOLATION se configuró con los valores NEAREST_NEIGHBOR , BILINEAR y BICUBIC

  • La imagen de entrada se convirtió a diferentes tipos:

    • BufferedImage.TYPE_INT_RGB : un tipo que se usa comúnmente, ya que "normalmente" muestra las mejores características de rendimiento

    • BufferedImage.TYPE_3BTE_BGR : este es el tipo con el que se lee de forma predeterminada, cuando solo se lee con ImageIO

  • El tamaño de la imagen de destino se varió entre un ancho de 10000 (por lo tanto, escalando la imagen hacia arriba ) y 100 (por lo tanto, reduciendo la escala de la imagen al tamaño de la miniatura)

Las pruebas se han ejecutado en un Win64 / AMD K10 con 3.7 GHz y JDK 1.8u31, con -Xmx4000m -server .

Los métodos probados son:

  • Usando AffineTransformOp , como en la respuesta de Jörn Horstmann
  • Usando una Graphics , como en la respuesta de johnstosh
  • Usando Image#getScaledInstance

El código de las pruebas se muestra aquí:

import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.function.Supplier; import javax.imageio.ImageIO; import javax.swing.JLabel; public class ImageScalingPerformance { private static int blackHole = 0; public static void main(String[] args) throws IOException { // Image with size 2560 x 1706, from https://upload.wikimedia.org/ // wikipedia/commons/4/41/Pitta_moluccensis_-_Kaeng_Krachan.jpg BufferedImage image = ImageIO.read( new File("Pitta_moluccensis_-_Kaeng_Krachan.jpg")); int types[] = { BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_INT_RGB, }; Object interpolationValues[] = { RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR, RenderingHints.VALUE_INTERPOLATION_BILINEAR, RenderingHints.VALUE_INTERPOLATION_BICUBIC, }; int widths[] = { 10000, 5000, 2500, 1000, 500, 100 }; System.out.printf("%10s%22s%6s%18s%10s/n", "Image type", "Interpolation", "Size", "Method", "Duration (ms)"); for (int type : types) { BufferedImage currentImage = convert(image, type); for (Object interpolationValue : interpolationValues) { for (int width : widths) { List<Supplier<Image>> tests = createTests(currentImage, interpolationValue, width); for (Supplier<Image> test : tests) { double durationMs = computeMs(test); System.out.printf("%10s%22s%6s%18s%10s/n", stringForBufferedImageType(type), stringForInterpolationValue(interpolationValue), String.valueOf(width), String.valueOf(test), String.format(Locale.ENGLISH, "%6.3f", durationMs)); } } } } System.out.println(blackHole); } private static List<Supplier<Image>> createTests( BufferedImage image, Object interpolationValue, int width) { RenderingHints renderingHints = new RenderingHints(null); renderingHints.put( RenderingHints.KEY_INTERPOLATION, interpolationValue); double scale = (double) width / image.getWidth(); int height = (int)(scale * image.getHeight()); Supplier<Image> s0 = new Supplier<Image>() { @Override public BufferedImage get() { return scaleWithAffineTransformOp( image, width, height, renderingHints); } @Override public String toString() { return "AffineTransformOp"; } }; Supplier<Image> s1 = new Supplier<Image>() { @Override public Image get() { return scaleWithGraphics( image, width, height, renderingHints); } @Override public String toString() { return "Graphics"; } }; Supplier<Image> s2 = new Supplier<Image>() { @Override public Image get() { return scaleWithGetScaledInstance( image, width, height, renderingHints); } @Override public String toString() { return "GetScaledInstance"; } }; List<Supplier<Image>> tests = new ArrayList<Supplier<Image>>(); tests.add(s0); tests.add(s1); tests.add(s2); return tests; } private static double computeMs(Supplier<Image> supplier) { int runs = 5; long before = System.nanoTime(); for (int i=0; i<runs; i++) { Image image0 = supplier.get(); blackHole += image0.hashCode(); } long after = System.nanoTime(); double durationMs = (after-before) / 1e6 / runs; return durationMs; } private static BufferedImage convert(BufferedImage image, int type) { BufferedImage newImage = new BufferedImage( image.getWidth(), image.getHeight(), type); Graphics2D g = newImage.createGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); return newImage; } private static BufferedImage scaleWithAffineTransformOp( BufferedImage image, int w, int h, RenderingHints renderingHints) { BufferedImage scaledImage = new BufferedImage(w, h, image.getType()); double scaleX = (double) w / image.getWidth(); double scaleY = (double) h / image.getHeight(); AffineTransform affineTransform = AffineTransform.getScaleInstance(scaleX, scaleY); AffineTransformOp affineTransformOp = new AffineTransformOp( affineTransform, renderingHints); return affineTransformOp.filter( image, scaledImage); } private static BufferedImage scaleWithGraphics( BufferedImage image, int w, int h, RenderingHints renderingHints) { BufferedImage scaledImage = new BufferedImage(w, h, image.getType()); Graphics2D g = scaledImage.createGraphics(); g.setRenderingHints(renderingHints); g.drawImage(image, 0, 0, w, h, null); g.dispose(); return scaledImage; } private static Image scaleWithGetScaledInstance( BufferedImage image, int w, int h, RenderingHints renderingHints) { int hint = Image.SCALE_REPLICATE; if (renderingHints.get(RenderingHints.KEY_ALPHA_INTERPOLATION) != RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) { hint = Image.SCALE_AREA_AVERAGING; } Image scaledImage = image.getScaledInstance(w, h, hint); MediaTracker mediaTracker = new MediaTracker(new JLabel()); mediaTracker.addImage(scaledImage, 0); try { mediaTracker.waitForAll(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return scaledImage; } private static String stringForBufferedImageType(int type) { switch (type) { case BufferedImage.TYPE_INT_RGB : return "INT_RGB"; case BufferedImage.TYPE_INT_ARGB : return "INT_ARGB"; case BufferedImage.TYPE_INT_ARGB_PRE : return "INT_ARGB_PRE"; case BufferedImage.TYPE_INT_BGR : return "INT_BGR"; case BufferedImage.TYPE_3BYTE_BGR : return "3BYTE_BGR"; case BufferedImage.TYPE_4BYTE_ABGR : return "4BYTE_ABGR"; case BufferedImage.TYPE_4BYTE_ABGR_PRE : return "4BYTE_ABGR_PRE"; case BufferedImage.TYPE_USHORT_565_RGB : return "USHORT_565_RGB"; case BufferedImage.TYPE_USHORT_555_RGB : return "USHORT_555_RGB"; case BufferedImage.TYPE_BYTE_GRAY : return "BYTE_GRAY"; case BufferedImage.TYPE_USHORT_GRAY : return "USHORT_GRAY"; case BufferedImage.TYPE_BYTE_BINARY : return "BYTE_BINARY"; case BufferedImage.TYPE_BYTE_INDEXED : return "BYTE_INDEXED"; } return "CUSTOM"; } private static String stringForInterpolationValue(Object value) { if (value == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) { return "NEAREST/REPLICATE"; } if (value == RenderingHints.VALUE_INTERPOLATION_BILINEAR) { return "BILINEAR/AREA_AVG"; } if (value == RenderingHints.VALUE_INTERPOLATION_BICUBIC) { return "BICUBIC/AREA_AVG"; } return "(unknown)"; } }

Primero, con respecto a getScaledInstance : como Chris Campbell ha señalado en su (famoso) artículo sobre The Perils of Image.getScaledInstance () (que ya estaba vinculado en otras respuestas), el método Image#getScaledInstance está algo roto, y tiene un efecto angustioso mal rendimiento para la mayoría de las configuraciones. Además, tiene la desventaja de no tener un control tan fino sobre el tipo de interpolación. Esto debe tenerse en cuenta en la siguiente comparación de rendimiento : la calidad de las imágenes resultantes puede diferir, lo que no se considera aquí. Por ejemplo, el método de "promedio de área" de getScaledInstance no produce una buena calidad de imagen cuando se aumenta el tamaño de la imagen.

(El inconveniente más grave de Image#getScaledInstance es IMHO que solo entrega una Image , y no una imagen BufferedImage , pero si la imagen solo se debe pintar en un Graphics , esto puede no ser importante)

Voy a volcar el resultado del programa aquí para referencia, algunos detalles serán los siguientes:

Image type Interpolation Size MethodDuration (ms) 3BYTE_BGR NEAREST/REPLICATE 10000 AffineTransformOp 197.287 3BYTE_BGR NEAREST/REPLICATE 10000 Graphics 184.427 3BYTE_BGR NEAREST/REPLICATE 10000 GetScaledInstance 1869.759 3BYTE_BGR NEAREST/REPLICATE 5000 AffineTransformOp 38.354 3BYTE_BGR NEAREST/REPLICATE 5000 Graphics 40.220 3BYTE_BGR NEAREST/REPLICATE 5000 GetScaledInstance 1088.448 3BYTE_BGR NEAREST/REPLICATE 2500 AffineTransformOp 10.153 3BYTE_BGR NEAREST/REPLICATE 2500 Graphics 9.461 3BYTE_BGR NEAREST/REPLICATE 2500 GetScaledInstance 613.030 3BYTE_BGR NEAREST/REPLICATE 1000 AffineTransformOp 2.137 3BYTE_BGR NEAREST/REPLICATE 1000 Graphics 1.956 3BYTE_BGR NEAREST/REPLICATE 1000 GetScaledInstance 464.989 3BYTE_BGR NEAREST/REPLICATE 500 AffineTransformOp 0.861 3BYTE_BGR NEAREST/REPLICATE 500 Graphics 0.750 3BYTE_BGR NEAREST/REPLICATE 500 GetScaledInstance 407.751 3BYTE_BGR NEAREST/REPLICATE 100 AffineTransformOp 0.206 3BYTE_BGR NEAREST/REPLICATE 100 Graphics 0.153 3BYTE_BGR NEAREST/REPLICATE 100 GetScaledInstance 385.863 3BYTE_BGR BILINEAR/AREA_AVG 10000 AffineTransformOp 830.097 3BYTE_BGR BILINEAR/AREA_AVG 10000 Graphics 1501.290 3BYTE_BGR BILINEAR/AREA_AVG 10000 GetScaledInstance 1627.934 3BYTE_BGR BILINEAR/AREA_AVG 5000 AffineTransformOp 207.816 3BYTE_BGR BILINEAR/AREA_AVG 5000 Graphics 376.789 3BYTE_BGR BILINEAR/AREA_AVG 5000 GetScaledInstance 1063.942 3BYTE_BGR BILINEAR/AREA_AVG 2500 AffineTransformOp 52.362 3BYTE_BGR BILINEAR/AREA_AVG 2500 Graphics 95.041 3BYTE_BGR BILINEAR/AREA_AVG 2500 GetScaledInstance 612.660 3BYTE_BGR BILINEAR/AREA_AVG 1000 AffineTransformOp 9.121 3BYTE_BGR BILINEAR/AREA_AVG 1000 Graphics 15.749 3BYTE_BGR BILINEAR/AREA_AVG 1000 GetScaledInstance 452.578 3BYTE_BGR BILINEAR/AREA_AVG 500 AffineTransformOp 2.593 3BYTE_BGR BILINEAR/AREA_AVG 500 Graphics 4.237 3BYTE_BGR BILINEAR/AREA_AVG 500 GetScaledInstance 407.661 3BYTE_BGR BILINEAR/AREA_AVG 100 AffineTransformOp 0.275 3BYTE_BGR BILINEAR/AREA_AVG 100 Graphics 0.297 3BYTE_BGR BILINEAR/AREA_AVG 100 GetScaledInstance 381.835 3BYTE_BGR BICUBIC/AREA_AVG 10000 AffineTransformOp 3015.943 3BYTE_BGR BICUBIC/AREA_AVG 10000 Graphics 5431.703 3BYTE_BGR BICUBIC/AREA_AVG 10000 GetScaledInstance 1654.424 3BYTE_BGR BICUBIC/AREA_AVG 5000 AffineTransformOp 756.136 3BYTE_BGR BICUBIC/AREA_AVG 5000 Graphics 1359.288 3BYTE_BGR BICUBIC/AREA_AVG 5000 GetScaledInstance 1063.467 3BYTE_BGR BICUBIC/AREA_AVG 2500 AffineTransformOp 189.953 3BYTE_BGR BICUBIC/AREA_AVG 2500 Graphics 341.039 3BYTE_BGR BICUBIC/AREA_AVG 2500 GetScaledInstance 615.807 3BYTE_BGR BICUBIC/AREA_AVG 1000 AffineTransformOp 31.351 3BYTE_BGR BICUBIC/AREA_AVG 1000 Graphics 55.914 3BYTE_BGR BICUBIC/AREA_AVG 1000 GetScaledInstance 451.808 3BYTE_BGR BICUBIC/AREA_AVG 500 AffineTransformOp 8.422 3BYTE_BGR BICUBIC/AREA_AVG 500 Graphics 15.028 3BYTE_BGR BICUBIC/AREA_AVG 500 GetScaledInstance 408.626 3BYTE_BGR BICUBIC/AREA_AVG 100 AffineTransformOp 0.703 3BYTE_BGR BICUBIC/AREA_AVG 100 Graphics 0.825 3BYTE_BGR BICUBIC/AREA_AVG 100 GetScaledInstance 382.610 INT_RGB NEAREST/REPLICATE 10000 AffineTransformOp 330.445 INT_RGB NEAREST/REPLICATE 10000 Graphics 114.656 INT_RGB NEAREST/REPLICATE 10000 GetScaledInstance 2784.542 INT_RGB NEAREST/REPLICATE 5000 AffineTransformOp 83.081 INT_RGB NEAREST/REPLICATE 5000 Graphics 29.148 INT_RGB NEAREST/REPLICATE 5000 GetScaledInstance 1117.136 INT_RGB NEAREST/REPLICATE 2500 AffineTransformOp 22.296 INT_RGB NEAREST/REPLICATE 2500 Graphics 7.735 INT_RGB NEAREST/REPLICATE 2500 GetScaledInstance 436.779 INT_RGB NEAREST/REPLICATE 1000 AffineTransformOp 3.859 INT_RGB NEAREST/REPLICATE 1000 Graphics 2.542 INT_RGB NEAREST/REPLICATE 1000 GetScaledInstance 205.863 INT_RGB NEAREST/REPLICATE 500 AffineTransformOp 1.413 INT_RGB NEAREST/REPLICATE 500 Graphics 0.963 INT_RGB NEAREST/REPLICATE 500 GetScaledInstance 156.537 INT_RGB NEAREST/REPLICATE 100 AffineTransformOp 0.160 INT_RGB NEAREST/REPLICATE 100 Graphics 0.074 INT_RGB NEAREST/REPLICATE 100 GetScaledInstance 126.159 INT_RGB BILINEAR/AREA_AVG 10000 AffineTransformOp 1019.438 INT_RGB BILINEAR/AREA_AVG 10000 Graphics 1230.621 INT_RGB BILINEAR/AREA_AVG 10000 GetScaledInstance 2721.918 INT_RGB BILINEAR/AREA_AVG 5000 AffineTransformOp 254.616 INT_RGB BILINEAR/AREA_AVG 5000 Graphics 308.374 INT_RGB BILINEAR/AREA_AVG 5000 GetScaledInstance 1269.898 INT_RGB BILINEAR/AREA_AVG 2500 AffineTransformOp 68.137 INT_RGB BILINEAR/AREA_AVG 2500 Graphics 80.163 INT_RGB BILINEAR/AREA_AVG 2500 GetScaledInstance 444.968 INT_RGB BILINEAR/AREA_AVG 1000 AffineTransformOp 13.093 INT_RGB BILINEAR/AREA_AVG 1000 Graphics 15.396 INT_RGB BILINEAR/AREA_AVG 1000 GetScaledInstance 211.929 INT_RGB BILINEAR/AREA_AVG 500 AffineTransformOp 3.238 INT_RGB BILINEAR/AREA_AVG 500 Graphics 3.689 INT_RGB BILINEAR/AREA_AVG 500 GetScaledInstance 159.688 INT_RGB BILINEAR/AREA_AVG 100 AffineTransformOp 0.329 INT_RGB BILINEAR/AREA_AVG 100 Graphics 0.277 INT_RGB BILINEAR/AREA_AVG 100 GetScaledInstance 127.905 INT_RGB BICUBIC/AREA_AVG 10000 AffineTransformOp 4211.287 INT_RGB BICUBIC/AREA_AVG 10000 Graphics 4712.587 INT_RGB BICUBIC/AREA_AVG 10000 GetScaledInstance 2830.749 INT_RGB BICUBIC/AREA_AVG 5000 AffineTransformOp 1069.088 INT_RGB BICUBIC/AREA_AVG 5000 Graphics 1182.285 INT_RGB BICUBIC/AREA_AVG 5000 GetScaledInstance 1155.663 INT_RGB BICUBIC/AREA_AVG 2500 AffineTransformOp 263.003 INT_RGB BICUBIC/AREA_AVG 2500 Graphics 297.663 INT_RGB BICUBIC/AREA_AVG 2500 GetScaledInstance 444.497 INT_RGB BICUBIC/AREA_AVG 1000 AffineTransformOp 42.841 INT_RGB BICUBIC/AREA_AVG 1000 Graphics 48.605 INT_RGB BICUBIC/AREA_AVG 1000 GetScaledInstance 209.261 INT_RGB BICUBIC/AREA_AVG 500 AffineTransformOp 11.004 INT_RGB BICUBIC/AREA_AVG 500 Graphics 12.407 INT_RGB BICUBIC/AREA_AVG 500 GetScaledInstance 156.794 INT_RGB BICUBIC/AREA_AVG 100 AffineTransformOp 0.817 INT_RGB BICUBIC/AREA_AVG 100 Graphics 0.790 INT_RGB BICUBIC/AREA_AVG 100 GetScaledInstance 128.700

Se puede observar que para casi todos los casos, getScaledInstance tiene un getScaledInstance bajo en comparación con los otros enfoques (y los pocos casos en los que parece funcionar mejor se pueden explicar por la menor calidad cuando se escala).

El AffineTransformOp basado en AffineTransformOp parece tener el mejor rendimiento en promedio, con la única excepción notable de una escalación TYPE_INT_RGB imágenes TYPE_INT_RGB , donde el enfoque basado en Graphics parece ser consistentemente más rápido.

El resultado final es: El método que utiliza AffineTransformOp , como en la respuesta de Jörn Horstmann , parece ser el que ofrece el mejor rendimiento para la mayoría de los casos de aplicación.


Estoy usando un código similar al siguiente para escalar imágenes, eliminé la parte que trata de preservar la relación de aspecto. El rendimiento fue definitivamente mejor que 10 por imagen, pero no recuerdo ningún número exacto. Para archivar una mejor calidad al reducir la escala, debe escalar en varios pasos si la imagen original es más del doble del tamaño de la miniatura deseada, cada paso debe escalar la imagen anterior a aproximadamente la mitad de su tamaño.

public static BufferedImage getScaledImage(BufferedImage image, int width, int height) throws IOException { int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); double scaleX = (double)width/imageWidth; double scaleY = (double)height/imageHeight; AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); return bilinearScaleOp.filter( image, new BufferedImage(width, height, image.getType())); }


La manera más rápida de escalar una imagen en java sin perder calidad de imagen es usar escalado bilineal. Bilineal solo es bueno si escala la imagen en un 50% a la vez debido a la forma en que funciona. El siguiente código es de ''Filthy rich clients'' por Chet Haase. Explica varias técnicas en el libro, pero esta tiene el más alto rendimiento para la compensación de calidad.

Admite todos los tipos de imágenes almacenadas, por lo que no debe preocuparse por la compatibilidad. También permite que el hardware java2D acelere su imagen porque los cálculos son realizados por Java2D. No te preocupes si no entiendes esa última parte. Lo más importante es que esta es la forma más rápida de hacerlo.

public static BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, boolean progressiveBilinear) { int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; BufferedImage ret = (BufferedImage) img; BufferedImage scratchImage = null; Graphics2D g2 = null; int w, h; int prevW = ret.getWidth(); int prevH = ret.getHeight(); if(progressiveBilinear) { w = img.getWidth(); h = img.getHeight(); }else{ w = targetWidth; h = targetHeight; } do { if (progressiveBilinear && w > targetWidth) { w /= 2; if(w < targetWidth) { w = targetWidth; } } if (progressiveBilinear && h > targetHeight) { h /= 2; if (h < targetHeight) { h = targetHeight; } } if(scratchImage == null) { scratchImage = new BufferedImage(w, h, type); g2 = scratchImage.createGraphics(); } g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null); prevW = w; prevH = h; ret = scratchImage; } while (w != targetWidth || h != targetHeight); if (g2 != null) { g2.dispose(); } if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) { scratchImage = new BufferedImage(targetWidth, targetHeight, type); g2 = scratchImage.createGraphics(); g2.drawImage(ret, 0, 0, null); g2.dispose(); ret = scratchImage; } System.out.println("ret is "+ret); return ret; }


Puede usar ImageMagick para crear miniaturas .

convert -define jpeg:size=500x180 hatching_orig.jpg -auto-orient / -thumbnail 250x90 -unsharp 0x.5 thumbnail.gif

Para usarlo desde Java, puede probar JMagick que proporciona una interfaz Java (JNI) para ImageMagick. O simplemente puede invocar los comandos de ImageMagick directamente usando Runtime.exec o ProcessBuilder .


Si quiere algo rápido, probablemente sea mejor con algún código nativo, si puede renunciar a la portabilidad.

Pero si quiere una solución pura de Java, puede probar algunas otras soluciones también, como Graphics2D.scale e Image.getScaledInstance . Los he usado en el pasado, pero no recuerdo cuál tuvo mejor rendimiento o mejores resultados, lo siento.

Pruébelos y vea cuál se ajusta mejor a sus necesidades.


Una vieja pregunta, pero en caso de que alguien más acierte a este problema: perfilé tu código y tu cuello de botella más grande es la llamada a:

Image.getScaledInstance()

Se sabe que esa llamada es horriblemente lenta. Por favor, esté convencido leyendo este documento:

Los peligros de Image.getScaledInstance ()

La solución más simple / mejor para una mejora dramática del rendimiento sería reemplazar esa llamada. Puede usar el método de la respuesta de dpineda (vea su respuesta / código arriba):

private BufferedImage getScaledImage(BufferedImage src, int w, int h){

Probé su método y funciona realmente bien. En mi prueba, su implementación (que evita la lenta Image.getScaledInstance ()) afeitó el 80% del tiempo de procesamiento.


esto funciona para mí:

private BufferedImage getScaledImage(BufferedImage src, int w, int h){ int original_width = src.getWidth(); int original_height = src.getHeight(); int bound_width = w; int bound_height = h; int new_width = original_width; int new_height = original_height; // first check if we need to scale width if (original_width > bound_width) { //scale width to fit new_width = bound_width; //scale height to maintain aspect ratio new_height = (new_width * original_height) / original_width; } // then check if we need to scale even with the new height if (new_height > bound_height) { //scale height to fit instead new_height = bound_height; //scale width to maintain aspect ratio new_width = (new_height * original_width) / original_height; } BufferedImage resizedImg = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = resizedImg.createGraphics(); g2.setBackground(Color.WHITE); g2.clearRect(0,0,new_width, new_height); g2.drawImage(src, 0, 0, new_width, new_height, null); g2.dispose(); return resizedImg; }

también agregué el fondo blanco para png


im4java con GraphicsMagick para obtener resultados realmente más rápidos (más rápido que ImageIO).

Usó ese tipo de código:

public static void createFilePreview(final File originalFile, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException { runThumbnail(new ConvertCmd(), originalFile.getAbsolutePath(), originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight); } public static void createFilePreview(final InputStream originalFileInputStream, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException { final ConvertCmd cmd = new ConvertCmd(); cmd.setInputProvider(new Pipe(originalFileInputStream, null)); runThumbnail(cmd, "-", originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight); } private static void runThumbnail(final ConvertCmd cmd, final String originalFile, final String originalFileMimeType, final String destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException { final IMOperation operation = new IMOperation(); // if it is a PDF, will add some optional parameters to get nicer results if (originalFileMimeType.startsWith("application/pdf")) { operation.define("pdf:use-trimbox=true"); // as it is said here http://www.prepressure.com/pdf/basics/page_boxes "The imposition programs and workflows that I know all use the TrimBox as the basis for positioning pages on a press sheet." operation.density(300, 300); // augment the rendering from 75 (screen size) to 300 dpi in order to create big preview with good quality } operation.addImage("[0]"); // if it is a PDF or other multiple image source, will extract the first page / image, else it is ignored operation.autoOrient(); // Auto-orient the image if it contains some orientation information (typically JPEG with EXIF header) operation.thumbnail(maxWidth, maxHeight); operation.addImage(); cmd.run(operation, originalFile, destinationPreviewFile); }