2010-10-19 13 views
44

Tengo una aplicación donde los usuarios pueden cargar imágenes en álbumes pero, naturalmente, las imágenes cargadas deben cambiar de tamaño para que también estén disponibles los pulgares y las imágenes mostradas también cabe en la página (p. ej., 800x600). La forma en que hago el cambio de tamaño es la siguiente:Cómo mejorar el rendimiento del método g.drawImage() para cambiar el tamaño de las imágenes

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 okayish. 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.

Respuesta

10

Puede usar ImageMagick para create thumbnails.

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.

3

Alguna vez tendrá una compensación entre la velocidad del cambio de tamaño y la calidad de la imagen resultante. Puede intentar con 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.

14

¿Realmente necesita la calidad que proporciona 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.

+0

SCALE_FAST no da suficiente calidad por desgracia, pero gracias por el artículo, que se ve muy útil, voy a intentarlo. –

0

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

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

Pruébelas y vea cuál se adapta mejor a sus necesidades.

24

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())); 
} 
+1

¿Tiene que lanzar IOException? – user1991679

+0

@ user1991679: probablemente extraje ese fragmento del código que también estaba guardando la imagen escalada, la excepción IOEx parece innecesaria si la imagen acaba de ser devuelta. Hubiera sido más rápido copiar el código en su IDE en lugar de preguntar :) –

+0

Gracias @ JörnHorstmann – ozgur

1

Algunas mejoras en el rendimiento (quizás pequeña, tal vez insignificante, tal vez a expensas de la calidad) puede alcanzarse mediante la deformación de los consejos de renderizado. P.ej.

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

Bueno, Jacob y yo queríamos cambiar el tamaño de 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; 
} 
+0

Hay un artículo bastante bueno sobre esta solución aquí: http://today.java.net/pub/a/today /2007/04/03/perils-of-image-getscaledinstance.html – Parker

+78

¿Quién es Jacob? – jasop

+4

No, en serio, ¿quién es Jacob? – Jesus

0

Solía ​​im4java con GraphicsMagick con el fin de tener resultados muy rápidos (más rápido que ImageIO).

utilizado esa especie 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); 
} 
4

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 he añadido el fondo blanco para png

+0

Confirmo que esto también funciona bien para mí. Lo principal en esta respuesta es que dpineda NO usa Image.getScaledInstance() que se sabe que es terriblemente lento. No usar esa llamada es esencialmente lo que lo hace más rápido. Ver detalles adicionales en mi (Pierre) otra respuesta a continuación. – Pierre

5

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, así que no se preocupe 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; 
} 
+0

¡Funcionó bien, funcionó para mí! –

8

El punto principal de la pregunta fue 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, probar el rendimiento de escalado de imagen confiablemente, sensiblemente y objetivamente es difícil. Hay demasiados factores infuencing a tener en cuenta:

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

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

  • La entrada es un simple, foto "promedio" (en particular, this "Image Of The Day" de Wikipedia, con un tamaño de 2560x1706 píxeles)

  • Los principales tipos de interpolación se prueban - a saber, por usando RenderingHints donde la clave INTERPOLATION se ajustó a los valores NEAREST_NEIGHBOR, BILINEAR y BICUBIC

  • la imagen de entrada se convirtió en diferentes tipos:

    • BufferedImage.TYPE_INT_RGB: Un tipo que se utiliza comúnmente, ya que "normalmente" muestra los mejores características de rendimiento

    • BufferedImage.TYPE_3BTE_BGR: Este es el tipo que se lee con forma predeterminada, cuando se acaba de leer con ImageIO

  • el tamaño de la imagen objetivo se varió entre una anchura de 10 000 (por lo tanto, escalar la imagen hasta ), y 100 (así, escalar la imagen a un tamaño 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 de prueba son:

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)"; 
    } 


} 

En primer lugar, en relación con getScaledInstance: Como Chris Campbell ha señalado en su (famoso) artículo sobre The Perils of Image.getScaledInstance() (que ya estaba vinculado a en otras respuestas), el método Image#getScaledInstance se rompe un poco, y tiene un penosamente mal desempeño de la mayoría configuraciones Además, tiene la desventaja de no tener un control tan fino sobre el tipo de interpolación. Esto se debe tener en cuenta en la siguiente comparación de rendimiento: calidad de las imágenes resultantes pueden diferir, lo que no se considera aquí. Por ejemplo, el método de "promedio de área" getScaledInstance no produce una buena calidad de imagen cuando el tamaño de la imagen es aumentó.

(El inconveniente más grave de Image#getScaledInstance es mi humilde opinión que sólo ofrece una Image, y no un BufferedImage, pero si la imagen se supone que sólo se va a pintar en un Graphics, esto puede no ser importante)

voy a volcar la salida del programa aquí por referencia, algunos detalles se darán a continuación:

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 realiza mal en comparación con la otra ap enfoques (y los pocos casos en los que parece tener un mejor rendimiento se pueden explicar por la menor calidad cuando se escala).

El enfoque basado AffineTransformOp parece funcionar mejor en promedio, con la única excepción notable que es una escala de NEAREST_NEIGHBORTYPE_INT_RGB imágenes, donde el enfoque basado en Graphics parece ser consistentemente más rápido.

La conclusión es: El método que utiliza AffineTransformOp, como en el answer by Jörn Horstmann, parece ser la que ofrece el mejor rendimiento para más casos de aplicación.

1

pregunta antiguo, pero en caso de que alguien llega a este tema: Me perfilado su código y el mayor cuello de botella es la llamada a:

Image.getScaledInstance() 

Esa llamada es bien conocido por ser terriblemente lento. Por favor, ser convencido por la lectura de este documento:

The Perils of Image.getScaledInstance()

El más simple/mejor solución para una mejora espectacular el rendimiento sería sustituir esa llamada. Usted puede utilizar el método de la respuesta de dpineda (véase su respuesta/código anterior):

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

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

Cuestiones relacionadas