14

我有一个将照片上传到 Facebook 的开源应用程序。为了节省带宽,照片会在上传前自动调整大小(Facebook 有最大大小限制)。一些人抱怨照片质量,实际上您可以看到差异(有关一些演示图像,请参阅此问题)。

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

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

4

7 回答 7

16

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

  • 避免 JDK 不太支持的 BufferedImage 类型。
  • 使用增量缩放
  • 使用增量缩放时坚持双三次

我已经使用这些方法进行了相当多的测试,并且增量缩放以及坚持良好支持的图像类型是关键——我看到亚历山大提到他仍然没有得到好运,这真是太可惜了。

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

标准用法如下所示:

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

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

您想要的用例,例如,如果 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。

于 2011-07-05T16:03:05.833 回答
12

我已经尝试了所有方法——包括这里的技巧,我只能说你最好在任何界面上使用 ImageMagick。在这方面,Javas 成像库根本无法满足。您需要支持如此多的格式和算法才能使其正确。

于 2009-10-26T14:54:16.007 回答
9

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

此外,还有 Java 的 JDK 方式Graphics2D请参阅this question on how to do it ),这是臭名昭著的,尤其是在缩小规模时会产生不好的结果。ImageMagick还有一个Java 接口,这里将省略,因为它需要一个外部工具。

视觉质量

这是将580x852png 大小调整/缩小为145x213. 作为参考,使用了 Photoshop CS5“保存为网络”调整大小。注意:结果是刚刚创建的库复制在一起的 1:1。缩放不使用任何过滤,只是一个简单的最近邻算法。 在这里您可以找到原始图像。

比较

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

我留给读者选择最佳结果,因为这是主观的。一般来说,除了Graphics2D. Thumbnailator 生成与 Photoshop 输出非常相似的更清晰的图像,而 imgscalr 的输出要柔和得多。对于图标/文本等,您需要更清晰的输出,对于图片,您可能需要更柔和的输出。

计算时间

这是使用此工具的非科学基准和 114 张图像,尺寸从大约96x962560x1440将其视为 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
  • 图形2D: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 秒。

于 2016-03-29T21:05:14.380 回答
5

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

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

于 2012-01-27T06:11:59.260 回答
2

What rendering hint are you using? Usually bicubic resampling will be the best. In the photos you are linking to they are very jaggy, which makes me think you are using nearest neighbor as your hint.

In the PictureScaler class that you link to, in the paintComponent method, it uses six different means of resizing the image. Have you tried all six to see which gives the best result?

于 2009-10-26T14:39:50.560 回答
0

我希望在保留纵横比的情况下进行最高质量的调整。尝试了几件事并阅读了几个条目。迷失了两天,最后我用普通的 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();
    }
  }
}
于 2013-05-31T14:16:09.763 回答
0

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

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

我将结果与在 Photoshop CS3 中完成的调整大小进行了比较,结果非常相似。

于 2009-12-01T11:25:32.137 回答