如何提高调整图像大小的g.drawImage()方法的性能

我有一个应用程序,用户可以在相册中上传图片,但自然上传的图片需要resize,所以也有大拇指可用,所显示的图片也适合页面(例如800×600)。 我resize的方式是这样的:

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

它的作品好吧。 我唯一的问题是, g.drawImage()方法似乎非常慢,我无法想象用户有足够的耐心等待20 * 10秒〜3分钟的20张图片上传。 事实上,在我的电脑上,对于单张照片制作3种不同的尺寸需要将近40秒的时间。

这不够好,我正在寻找一个更快的解决scheme。 我想知道是否有人可以通过调用shell脚本,命令告诉我一个更好的Java代码,无论你知道什么黑客,它必须更快,其他的一切都不重要。

您可以使用ImageMagick 创build缩略图 。

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

要从Java使用它,你可以尝试JMagick ,它提供了一个Java(JNI)接口ImageMagick。 或者您可以直接使用Runtime.execProcessBuilder直接调用ImageMagick命令。

我正在使用类似于下面的代码来缩放图像,我删除了处理保持宽高比的部分。 每张图片的performance绝对好于10秒,但我不记得确切的数字。 为了在缩小时存档更好的质量,如果原始图像是所需缩略图大小的两倍,则应分几步进行缩放,每一步都应将前一幅图像放大到其大小的一半。

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

你真的需要使用Image.SCALE_SMOOTH提供的质量吗? 如果你不这样做,你可以尝试使用Image.SCALE_FAST 。 如果你想坚持Java提供的东西,你可能会发现这篇文章很有帮助。

那么,雅各布和我想调整一个图像,而不是一个BufferedImage。 所以我们结束了这个代码:

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

这个问题的主要问题是关于在Java中缩放图像的性能。 其他答案显示了不同的方法,没有进一步评估。 我也很好奇,所以我试着写一个小的性能testing。 然而,要可靠地testing图像缩放性能, 明智客观地是困难的。 有太多的影响因素需要考虑:

  • input图像的大小
  • 输出图像的大小
  • 插值(即“质量”:最近邻,双线性,双三次)
  • input图像的BufferedImage.TYPE_*
  • 输出图像的BufferedImage.TYPE_*
  • JVM版本和操作系统
  • 最后:实际用于执行操作的方法。

我试图涵盖那些我认为是最重要的。 设置是:

  • input是一个简单的,“平均”的照片(特别是这个“维基百科”的“今日影像” ,尺寸为2560×1706像素)

  • 主要插值types被testing – 即通过使用RenderingHints ,其中INTERPOLATION键设置为值BICUBICBICUBICBICUBIC

  • input图像被转换为​​不同的types:

    • BufferedImage.TYPE_INT_RGB :通常使用的types,因为它“通常”显示最佳的性能特征

    • BufferedImage.TYPE_3BTE_BGR :这是它默认读取的types,当用ImageIO读取它时

  • 目标图像大小在10000的宽度(因此,缩放图像)和100(从而将图像缩小为缩略图大小)之间变化,

testing已经在Win64 / AMD K10上运行,频率为3.7 GHz,JDK 1.8u31和-Xmx4000m -server

testing方法是:

  • 使用AffineTransformOp ,就像JörnHorstmann的回答一样
  • 使用Graphics ,如johnstosh的答案
  • 使用Image#getScaledInstance

testing代码如下所示:

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

首先,关于getScaledInstance :正如Chris Campbell在其着名的关于Image.getScaledInstance( )的文章中指出的(它已经和其他答案中的链接相关), Image#getScaledInstance方法有些破裂,并且令人不安大多数configuration的性能不佳。 此外,它具有不具有关于插值types的细粒度控制的缺点。 在下面的性能比较中应该考虑到这一点 :所得图像的质量可能不同,这里不考虑。 例如,当图像大小增加时, getScaledInstance的“区域平均”方法不会产生良好的图像质量。

Image#getScaledInstance最严重的缺点是恕我直言,它只提供一个Image ,而不是一个BufferedImage ,但如果图像只应被绘成一个Graphics ,这可能并不重要)

我只是将程序的输出转储到这里供参考,一些细节如下:

 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 

可以看出,对于几乎所有的情况, getScaledInstance与其他方法相比performance不佳(而less数情况下,performance更好的情况可以通过缩放时的较低质量来解释)。

基于AffineTransformOp的方法似乎平均执行得最好,唯一值得注意的例外是TYPE_INT_RGB图像的NEAREST_NEIGHBOR缩放,其中基于Graphics的方法看起来一直更快。

底线是:使用AffineTransformOp的方法,如JörnHorstmann的答案 ,似乎是为大多数应用案例提供最佳性能的方法。

在不失去图像质量的情况下,用java缩放图像的最快方法是使用双线性缩放。 因为它的工作方式,一次只能将图像缩放50%,双线性效果不错。 以下代码来自Chet Haase的“肮脏的富客户端”。 他在书中解释了多种技术,但是这种技术在质量平衡方面具有最高的性能。

它支持所有types的BufferedImages,所以不要担心兼容性。 它还可以让java2D硬件加速您的映像,因为计算是由Java2D完成的。 不要担心,如果你不明白,最后一部分。 最重要的是这是最快的方法。

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

在resize的速度和结果图像的质量之间,你将永远有一个权衡。 您可以尝试JDK的另一个缩放algorithm。

图像编辑AFAIK的最好和最灵活的工具是ImageMagick 。

Java语言有两个接口:

  • JMagick – 是ImageMagick的JNI接口。 查看项目Wiki以获取更多信息。
  • im4java – 是ImageMagick的命令行界面。 它不像JMagick那样基于JNI。

在使用命令行直接调用ImageMagick之前,您应该首选im4java。

这对我有用:

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

我也为png添加了白色背景

通过调整渲染提示可以获得性能方面的一些改进(可能很小,也许可以忽略不计,也许以牺牲质量为代价)。 例如

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

老问题,但如果有人遇到这个问题:我简介你的代码,你最大的瓶颈是呼吁:

 Image.getScaledInstance() 

众所周知,这个呼叫非常缓慢。 请通过阅读本文件来确信:

Image.getScaledInstance()的风险

大幅改善性能的最简单/最好的解决scheme是取代这个呼叫。 你可以使用dpineda答案中的方法(见上面的答案/代码):

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

我testing了他的方法,它工作得很好。 在我的testing中,他的实现(避免了慢Image.getScaledInstance())削减了80%的处理时间!

如果你想要更快一些,如果你可以放弃可移植性的话,那么你可能会使用一些本地代码。

但是如果你想要一个纯粹的Java解决scheme,你可以尝试一些其他的解决scheme,比如Graphics2D.scale和Image.getScaledInstance 。 我以前用过它们,但不记得哪一个有更好的performance或更好的结果,对不起。

尝试一下,看看哪一个最适合你的需求。

我使用GraphicsMagick的 im4java为了有更快的结果(比ImageIO更快)。

使用那种代码:

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