Come migliorare le prestazioni del metodo g.drawImage () per ridimensionare le immagini

Ho un’applicazione in cui gli utenti sono in grado di caricare le foto negli album, ma naturalmente le immagini caricate devono essere ridimensionate, quindi ci sono anche i pollici disponibili e le immagini mostrate si adattano anche alla pagina (ad esempio 800×600). Il modo in cui faccio il ridimensionamento è come questo:

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(); 

E funziona bene. Il mio unico problema è che il metodo g.drawImage() sembra essere terribilmente lento e non riesco a immaginare che l’utente sia abbastanza paziente da aspettare un caricamento di 20 immagini di 20 * 10 secondi ~ 3 minuti. Infatti, sul mio computer ci vogliono quasi 40 secondi per realizzare 3 diverse dimensioni per una singola immagine.

Non è abbastanza buono, e sto cercando una soluzione più veloce. Mi chiedo se qualcuno potrebbe parlarmi di uno migliore in Java OPPURE chiamando uno script di shell, comando, qualunque trucco tu sappia, deve essere più veloce, tutto il resto non conta questa volta.

È ansible utilizzare ImageMagick per creare miniature .

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

Per usarlo da Java puoi provare JMagick che fornisce un’interfaccia Java (JNI) a ImageMagick. Oppure puoi semplicemente richiamare i comandi ImageMagick direttamente usando Runtime.exec o ProcessBuilder .

Sto usando un codice simile al seguente per ridimensionare le immagini, ho rimosso la parte che si occupa di preservare le proporzioni. La performance era decisamente migliore di 10s per immagine, ma non ricordo alcun numero esatto. Per archiviare una qualità migliore durante il downscaling, è necessario ridimensionare in diversi passaggi se l’immagine originale è più del doppio della miniatura desiderata, ogni passaggio dovrebbe ridimensionare l’immagine precedente a circa metà della sua dimensione.

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

Hai davvero bisogno della qualità che viene fornita utilizzando Image.SCALE_SMOOTH? Se non lo fai, puoi provare a utilizzare Image.SCALE_FAST . È ansible trovare questo articolo utile se si desidera attenersi a qualcosa fornito da Java.

Bene, io e Jacob volevamo ridimensionare un’immagine, non una BufferedImage. Quindi abbiamo finito con questo codice:

 /** * 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; } 

Il punto principale della domanda riguardava le prestazioni di ridimensionamento delle immagini in Java . Le altre risposte, hanno mostrato approcci diversi, senza valutarli ulteriormente. Anch’io ero curioso di questo, quindi ho provato a scrivere un piccolo test delle prestazioni. Tuttavia, testare le prestazioni di scalabilità dell’immagine in modo affidabile , ragionevole e oggettivo è difficile. Ci sono troppi fattori di influenza da prendere in considerazione:

  • La dimensione dell’immagine di input
  • La dimensione dell’immagine di output
  • L’interpolazione (cioè “qualità”: vicino più prossimo, bilineare, bicubico)
  • The BufferedImage.TYPE_* dell’immagine di input
  • Il BufferedImage.TYPE_* dell’immagine di output
  • La versione JVM e il sistema operativo
  • Infine: il metodo che viene effettivamente utilizzato per eseguire l’operazione.

Ho cercato di coprire quelli che considero i più importanti. Il setup era:

  • L’input è una semplice foto “media” (in particolare, questa “Immagine del giorno” da Wikipedia, con una dimensione di 2560×1706 pixel)

  • I principali tipi di interpolazione vengono testati, ovvero utilizzando RenderingHints dove la chiave INTERPOLATION stata impostata sui valori NEAREST_NEIGHBOR , BILINEAR e BICUBIC

  • L’immagine di input è stata convertita in diversi tipi:

    • BufferedImage.TYPE_INT_RGB : un tipo che viene comunemente utilizzato, in quanto “di solito” mostra le migliori caratteristiche di prestazione

    • BufferedImage.TYPE_3BTE_BGR : questo è il tipo con cui viene letto di default, quando lo si legge con ImageIO

  • La dimensione dell’immagine di destinazione era variata tra una larghezza di 10000 (quindi, ridimensionando l’immagine) e 100 (quindi, ridimensionando l’immagine fino alla dimensione della miniatura)

I test sono stati eseguiti su un Win64 / AMD K10 con 3.7 GHz e JDK 1.8u31, con -Xmx4000m -server .

I metodi testati sono:

  • Utilizzando un AffineTransformOp , come nella risposta di Jörn Horstmann
  • Usando una Graphics , come nella risposta di johnstosh
  • Utilizzando Image#getScaledInstance

Il codice dei test è mostrato qui:

 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> tests = createTests(currentImage, interpolationValue, width); for (Supplier 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> 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 s0 = new Supplier() { @Override public BufferedImage get() { return scaleWithAffineTransformOp( image, width, height, renderingHints); } @Override public String toString() { return "AffineTransformOp"; } }; Supplier s1 = new Supplier() { @Override public Image get() { return scaleWithGraphics( image, width, height, renderingHints); } @Override public String toString() { return "Graphics"; } }; Supplier s2 = new Supplier() { @Override public Image get() { return scaleWithGetScaledInstance( image, width, height, renderingHints); } @Override public String toString() { return "GetScaledInstance"; } }; List> tests = new ArrayList>(); tests.add(s0); tests.add(s1); tests.add(s2); return tests; } private static double computeMs(Supplier supplier) { int runs = 5; long before = System.nanoTime(); for (int i=0; i 

Innanzitutto, riguardo a getScaledInstance : Come Chris Campbell ha sottolineato nel suo (famoso) articolo su The Perils of Image.getScaledInstance () (che era già collegato ad altre risposte), il metodo Image#getScaledInstance è in qualche modo rotto e ha un aspetto angoscioso cattive prestazioni per la maggior parte delle configurazioni. Inoltre, ha lo svantaggio di non avere un controllo così fine sul tipo di interpolazione. Questo dovrebbe essere preso in considerazione nel seguente confronto delle prestazioni : la qualità delle immagini risultanti potrebbe essere diversa, il che non è considerato qui. Ad esempio, il metodo "area average" di getScaledInstance non produce una buona qualità dell'immagine quando viene aumentata la dimensione dell'immagine.

(Lo svantaggio più grave di Image#getScaledInstance è IMHO che fornisce solo Image , e non una BufferedImage , ma se si suppone che l'immagine venga dipinta in una Graphics , ciò potrebbe non essere importante)

Mi limiterò a scaricare l'output del programma qui per riferimento, alcuni dettagli seguiranno di seguito:

 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 

Si può notare che per quasi tutti i casi, getScaledInstance prestazioni scarse rispetto agli altri approcci (e i pochi casi in cui sembra funzionare meglio possono essere spiegati dalla minore qualità durante il ridimensionamento).

L'approccio basato su AffineTransformOp sembra avere prestazioni migliori in media, con l'unica eccezione notevole rappresentata NEAREST_NEIGHBOR ridimensionamento TYPE_INT_RGB immagini TYPE_INT_RGB , in cui l'approccio basato sulla Graphics sembra essere costantemente più veloce.

La linea di fondo è: il metodo che utilizza AffineTransformOp , come nella risposta di Jörn Horstmann , sembra essere quello che offre le migliori prestazioni per la maggior parte dei casi applicativi.

questo funziona per me:

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

inoltre ho aggiunto lo sfondo bianco per png

Il modo più rapido per ridimensionare un’immagine in java senza perdere la qualità dell’immagine è utilizzare il ridimensionamento Bilineare. Bilineare va bene solo se ridimensioni l’immagine del 50% alla volta a causa del suo funzionamento. Il seguente codice proviene da “Filthy rich client” di Chet Haase. Spiega diverse tecniche nel libro, ma questo ha le migliori prestazioni per un compromesso qualitativo.

Supporta tutti i tipi di BufferedImages quindi non preoccuparti della compatibilità. Consente inoltre all’hardware java2D di accelerare l’immagine perché i calcoli vengono eseguiti da Java2D. Non preoccuparti se non capisci quell’ultima parte. La cosa più importante è che questo è il modo più veloce per farlo.

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

Avrai sempre un compromesso tra la velocità del ridimensionamento e la qualità dell’immagine risultante. Potresti provare un altro algoritmo di ridimensionamento del JDK.

Lo strumento migliore e più flessibile per l’editing di immagini AFAIK è ImageMagick .

Ci sono due interfacce per il linguaggio Java:

  • JMagick – è un’interfaccia JNI per ImageMagick. Guarda i progetti Wiki per ottenere maggiori informazioni.
  • im4java – è un’interfaccia a riga di comando per ImageMagick. Non è, come JMagick, basato su JNI.

Dovresti preferire im4java prima di utilizzare la riga di comando direttamente per chiamare ImageMagick.

Vecchia domanda, ma nel caso in cui qualcun altro abbia riscontrato questo problema: ho profilato il tuo codice e il tuo collo di bottiglia più grande è la chiamata a:

 Image.getScaledInstance() 

Quella chiamata è ben nota per essere terribilmente lenta. Si prega di essere convinti leggendo questo documento:

I pericoli di Image.getScaledInstance ()

La soluzione più semplice / migliore per un miglioramento spettacolare delle prestazioni sarebbe quella di sostituire quella chiamata. Puoi usare il metodo dalla risposta di dpineda (vedi la sua risposta / codice sopra):

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

Ho provato il suo metodo e funziona davvero bene. Nei miei test, la sua implementazione (che evita il lento Image.getScaledInstance ()) ha ridotto l’80% del tempo di elaborazione!

Un miglioramento delle prestazioni (forse piccolo, forse trascurabile, forse a scapito della qualità) può essere raggiunto modificando i suggerimenti di rendering. Per esempio

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

Se vuoi qualcosa di veloce, probabilmente stai meglio con un codice nativo, se puoi rinunciare alla portabilità.

Ma se vuoi una soluzione Java pura, puoi provare anche altre soluzioni, come Graphics2D.scale e Image.getScaledInstance . Li ho usati in passato, ma non riesco a ricordare quali hanno prestazioni migliori o risultati migliori, mi dispiace.

Provali e vedi quale si adatta meglio alle tue esigenze.

Ho usato im4java con GraphicsMagick per avere risultati molto più veloci (più veloci di ImageIO).

Usato quel tipo di codice:

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