图像resize质量(Java)

我有一个开源应用程序,可以将照片上传到Facebook。 为了节省带宽,照片会在上传前自动resize(Facebook规定了最大尺寸限制)。 有些人抱怨照片质量,事实上你可以看到差异(对于一些演示图像,请参阅此问题 )。

所以我的问题是,在不降低质量的情况下缩小Java中的图像(即照片)的“最佳”方法是什么,或者至少在质量损失/伪影最小的情况下?

您可以在此处查看我当前的代码(通过此页面调整代码大小)。

我已经尝试了所有这些 – 包括这里的技巧,而且我可以说你最好在任何接口上使用ImageMagick,Javas成像库在这方面都不合适。 您需要支持如此多的格式和算法才能使其正确。

Phil,我不知道你最终选择了哪种解决方案,但是如果您:使用Java缩放图像看起来相当不错:

  • 避免使用JDK不支持的BufferedImage类型。
  • 使用增量缩放
  • 使用增量缩放时,请坚持使用bicubic

我已经用这些方法做了相当多的测试,增量缩放以及坚持支持良好的图像类型是关键 – 我看到亚历山大提到他仍然没有好运,这是一个无赖。

我大约6个月前发布了imgscalr库 (Apache 2),以解决“我想要这个图像的好看的缩放副本,现在就做!”的问题。 在读完类似10个这样的问题之后。

标准用法如下:

BufferedImage img = ImageIO.read(...); // load image BufferedImage scaledImg = Scalr.resize(img, 640); 

第二个参数是imgscalr用于缩放图像的边界宽度和高度 – 即使传入了无效的尺寸也保持其比例正确 – 有更多更详细的方法 ,但这是最简单的用法。

您想要的用例,例如,如果Facebook限制图像为800×600像素,将如下所示:

 BufferedImage img = ImageIO.read(...); // load image BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600); 

这将确保图像保持最佳支持的图像类型,并使用Java可以集合的最高质量方法进行缩放。

在我自己的高分辨率测试中,我没有注意到使用此库/这些方法与缩放图像之间存在任何差距。例如,当您的图像被ImageIO加载器置于支持不良的图像类型时 – 例如,GIF会发生这种情况很多。 如果你把它们留下来并且不能从那些支持不好的类型中取出它们,它最终看起来真的很眩晕和可怕。

这样做的原因是Java2D团队实际上为JDK可以处理的所有不同类型的BufferedImages提供了不同的硬件加速流水线 – 这些不太常见的图像类型的子集都可以归结为使用相同的软件渲染管道。覆盖Java2D,导致图像质量差,有时完全不正确。 这是一个PIA来解释并试图找出我刚刚将该逻辑直接写入库中。

如果您好奇,两个最受支持的类型是BufferedImage.TYPE_INT_RGB和_ARGB。

要使用自定义质量调整图像大小,请使用thumbnailator.jar。

示例代码http://code.google.com/p/thumbnailator/wiki/Examples

目前在Java中专门用于图像大小调整的两个最流行的开源库是:

  • Thumbnailator
  • imgscalr

Additonal有Java的Graphics2D的JDK方式( 请参阅这个问题,如何做到这一点 ),这是创建不良结果,特别是降尺度的臭名昭着。 ImageMagick还有一个Java接口,这里省略它,因为它需要一个外部工具。

视觉质量

以下是将580x852 pngresize/缩小到145x213的结果的比较。 作为参考,使用Photoshop CS5“save for web”resize。 注意:结果是1:1 libs创建的只是复制在一起的内容。 缩放不使用任何过滤,只使用简单的最近邻居算法。 在这里你可以找到原始图像。

对照

  1. Thumbnailator 0.4.8具有默认设置,无尺寸调整
  2. Photoshop CS5采用双三次算法
  3. imgscalr 4.2,ULTRA_QUALITY设置,无尺寸调整
  4. Graphics2D(Java 8),提示提示VALUE_INTERPOLATION_BICUBIC,VALUE_RENDER_QUALITY,VALUE_ANTIALIAS_ON

我留给读者选择最好的结果,因为这是主观的。 通常,除Graphics2D外,所有输出都有良好的输出。 Thumbnailator生成更清晰的图像,非常类似于Photoshop输出,而imgscalr的输出则相当柔和。 对于图标/文本等,您需要更清晰的输出,对于图片,您可能需要更柔和的输出。

计算时间

以下是使用此工具的非科学基准测试和尺寸从大约96x962560x1440 114张图像,将其视为425%图像创建:100%,150%,200%,300%和400%缩放版本(所以114 * 5缩放操作)。 所有库都使用与质量比较相同的设置(因此可能具有最高质量)。 时间只是缩放而不是整个过程。 在i5-2520M上完成8GB Ram和5次运行。

  • Thumbnailator :7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700.3ms
  • imgscalr :25218.5ms | 25786.6ms | 25095.7ms | 25790.4ms | 29296.3ms
  • Graphics2D :7387.6ms | 7177.0ms | 7048.2ms | 7132.3ms | 7510.3ms

以下是此基准测试中使用的代码。

有趣的是, Thumbnailator也是最快的,平均时间为6.9秒,其次是Java2D,7.2秒,留下imgscalr,差26.2秒 。 这可能不公平,因为imgscalr设置为ULTRA_QUALITY ,这似乎非常昂贵; 在QUALITY设置下,它的平均竞争力为11.1秒。

你使用什么渲染提示? 通常双三次重采样将是最好的。 在你链接到的照片中,它们非常锯齿状,这让我觉得你正在使用最近邻居作为你的提示。

在链接到的PictureScaler类中,在paintComponent方法中,它使用六种不同的方法来调整图像大小。 你有没有试过所有六个看到哪个给出了最好的结果?

经过一些令人沮丧的实验后,我发现了以下resize的评估 ,并在我的项目中采用了多遍方法。

为此,我将getScaledInstance()方法复制到我的缩略图生成器类中,更改了我的图像读取方法以使用ImageIO(一个返回BufferedImage),现在非常高兴

我将结果与Photoshop CS3中的resize进行了比较,结果非常相似。

我想要保留高宽比的最高质量调整。 尝试了几件事并阅读了几个条目。 失去了两天,最后我用普通的Java方法获得了最好的结果(同时尝试了ImageMagick和java-image-scaling库):

 public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException { BufferedImage sourceImage = ImageIO.read(new FileInputStream(source)); double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight(); if (width < 1) { width = (int) (height * ratio + 0.4); } else if (height < 1) { height = (int) (width /ratio + 0.4); } Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING); BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bufferedScaled.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2d.drawImage(scaled, 0, 0, width, height, null); dest.createNewFile(); writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f); return true; } /** * Write a JPEG file setting the compression quality. * * @param image a BufferedImage to be saved * @param destFile destination file (absolute or relative path) * @param quality a float between 0 and 1, where 1 means uncompressed. * @throws IOException in case of problems writing the file */ private static void writeJpeg(BufferedImage image, String destFile, float quality) throws IOException { ImageWriter writer = null; FileImageOutputStream output = null; try { writer = ImageIO.getImageWritersByFormatName("jpeg").next(); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); output = new FileImageOutputStream(new File(destFile)); writer.setOutput(output); IIOImage iioImage = new IIOImage(image, null, null); writer.write(null, iioImage, param); } catch (IOException ex) { throw ex; } finally { if (writer != null) { writer.dispose(); } if (output != null) { output.close(); } } }