【发布时间】:2010-12-10 03:43:27
【问题描述】:
我有一个将照片上传到 Facebook 的开源应用程序。为了节省带宽,照片会在上传前自动调整大小(Facebook 有最大大小限制)。一些人抱怨照片质量,实际上您可以看到差异(请参阅this issue 获取一些演示图像)。
所以我的问题是,什么是在 Java 中缩小图像(即照片)而不损失质量的“最佳”方法,或者至少以最小的质量损失/伪影?
【问题讨论】:
标签: java image resize jpeg photo
我有一个将照片上传到 Facebook 的开源应用程序。为了节省带宽,照片会在上传前自动调整大小(Facebook 有最大大小限制)。一些人抱怨照片质量,实际上您可以看到差异(请参阅this issue 获取一些演示图像)。
所以我的问题是,什么是在 Java 中缩小图像(即照片)而不损失质量的“最佳”方法,或者至少以最小的质量损失/伪影?
【问题讨论】:
标签: java image resize jpeg photo
您使用什么渲染提示?通常双三次重采样是最好的。在您链接到的照片中,它们非常锯齿,这让我认为您正在使用最近的邻居作为提示。
在您链接到的PictureScaler 类中,在paintComponent 方法中,它使用六种不同的方法来调整图像大小。您是否尝试了所有六种方法,看看哪一种效果最好?
【讨论】:
我已经尝试了所有方法——包括技巧here,我只能说你最好在任何界面上使用 ImageMagick。在这方面,Javas 成像库根本无法满足。您需要支持如此多的格式和算法才能使其正确。
【讨论】:
经过几次令人沮丧的实验后,我发现了以下resize evaluation,并在我的项目中采用了多通道方法。
为此,我将 getScaledInstance() 方法复制到缩略图生成器类中,将图像读取方法更改为使用 ImageIO(返回 BufferedImage),现在我非常高兴!
我将结果与在 Photoshop CS3 中调整大小进行了比较,结果非常相似。
【讨论】:
Phil,我不知道您最终采用了哪种解决方案,但是如果您使用 Java 缩放图像看起来会很不错:
我已经使用这些方法进行了相当多的测试,而增量缩放以及坚持支持良好的图像类型是关键 - 我看到 Alexander 提到他仍然没有获得好运,这真是太可惜了.
大约 6 个月前,我发布了imgscalr library (Apache 2),以解决“我想要这张图片的漂亮缩放副本,现在就做!”的问题。在 SO 上阅读了类似 10 个这样的问题之后。
标准用法如下:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 640);
第二个参数是 imgscalr 将用于缩放图像的边界宽度和高度 - 即使您传入无效的尺寸,也要保持其比例正确 - 有很多 more detailed methods,但这是最简单的用法。
您想要的用例(例如 Facebook 将图像限制为 800x600 像素)如下所示:
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。
【讨论】:
我希望在保留纵横比的情况下进行最高质量的调整。 尝试了几件事并阅读了几个条目。迷失了两天,最后我用普通的 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();
}
}
}
【讨论】:
目前两个最流行的专门用于在 java 中调整图像大小的开源库是:
此外,还有 Java 的 Graphics2D (see this question on how to do it) 的 JDK 方式,它因创建 bad results especially with downscaling 而臭名昭著。还有一个Java interface to ImageMagick,这里省略,因为它需要外部工具。
这是将580x852 png 调整为145x213 的结果的比较。作为参考,使用了 Photoshop CS5“保存为网络”调整大小。 注意:结果是刚刚创建的库复制在一起的 1:1。缩放不使用任何过滤,只是一个简单的最近邻算法。Here you can find the original image.
- Thumbnailator 0.4.8,默认设置,无尺寸调整
- 带有双三次算法的 Photoshop CS5
- imgscalr 4.2 具有 ULTRA_QUALITY 设置,无尺寸调整
- Graphics2D (Java 8) 带有渲染提示 VALUE_INTERPOLATION_BICUBIC、VALUE_RENDER_QUALITY、VALUE_ANTIALIAS_ON
我让读者选择最佳结果,因为这是主观的。一般来说,除了Graphics2D之外,所有的输出都很好。 Thumbnailator 生成与 Photoshop 输出非常相似的更清晰的图像,而 imgscalr 的输出要柔和得多。对于图标/文本等,您需要更清晰的输出,对于图片,您可能需要更柔和的输出。
这是使用 tool 和 114 张图像的非科学基准,尺寸从大约 96x96 到 2560x1440 将其视为 425% 图像创建:100%、150%、200%、300% 和 400它的 % 缩放版本(所以 114 * 5 缩放操作)。所有库都使用与质量比较相同的设置(因此可能达到最高质量)。时间只是缩放而不是整个过程。在具有 8GB 内存和 5 次运行的 i5-2520M 上完成。
- 缩略图器:7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700.3ms
- imgscalr:25218.5ms | 25786.6ms | 25095.7ms | 25790.4ms | 29296.3ms
- Graphics2D:7387.6 毫秒 | 7177.0ms | 7048.2ms | 7132.3ms | 7510.3ms
Here is the code used in this benchmark.
有趣的是,Thumbnailator 也是最快的,平均时间为 6.9 秒,其次是 Java2D,为 7.2 秒,imgscalr 以 26.2 秒落后 >。这可能不公平,因为 imgscalr 设置为 ULTRA_QUALITY,这似乎非常昂贵;使用QUALITY 设置,它的平均时间为更具竞争力的 11.1 秒。
【讨论】: