图像调整大小质量(Java)

时间:2009-10-26 14:28:06

标签: java image resize jpeg photo

我有一个开源应用程序,可以将照片上传到Facebook。为了节省带宽,照片会在上传前自动调整大小(Facebook规定了最大大小限制)。有些人抱怨照片质量,事实上你可以看到差异(参见this issue了解一些演示图片)。

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

您可以看到我当前的代码here(通过this page调整代码大小)。

7 个答案:

答案 0 :(得分:16)

菲尔,我不知道你最终选择了哪种解决方案,但如果你这样做,用Java缩放图像看起来会很好:

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

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

我在大约6个月前发布了imgscalr library(Apache 2),以解决“我想要这张图片的好看的缩放副本,现在就开始!”的问题。在读完这样的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。

答案 1 :(得分:12)

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

答案 2 :(得分:6)

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

Additonal使用Java Graphics2Dsee this question on how to do it)的JDK方式,这是创建bad results especially with downscaling的臭名昭着。此处还会省略Java interface to ImageMagick,因为它需要外部工具。

视觉质量

以下是将------WebKitFormBoundary6CDiwAgytdoviD7R Content-Disposition: form-data; name="data" {"key":"logos/TEST.png","acl":"public-read","policy":"eyJleHBpcmF0aW9uIjoiMjAzMC0xMi0zMVQxMjowMDowMC4wMDBaIiwiY29uZGl0aW9ucyI6W3siYWNsIjoicHVibGljLXJlYWQifSx7ImJ1Y2tldCI6ImhhdmVuLWRldi1pbWFnZXMifSxbInN0YXJ0cy13aXRoIiwiJGtleSIsImxvZ29zLyJdXX0=","X-amz-credential":"AKIAJAXCR6PRBXGHLU4Q/20160401/us-west-1/s3/aws4_request","X-amz-algorithm":"AWS4-HMAC-SHA256","X-amz-signature":"674d747f7e83375a1742c1a42cfd08799ffed7223aa9a255f4ea5eaad0df4470","Content-Type":"image/png","filename":"TEST.png","file":{}} ------WebKitFormBoundary6CDiwAgytdoviD7R-- png调整大小/降级到580x852的结果的比较。作为参考,使用Photoshop CS5“save for web”调整大小。 注意:结果是1:1 libs创建的只是复制在一起。缩放不使用任何过滤,只使用简单的最近邻居算法。 Here you can find the original image.

comparison

  
      
  1. Thumbnailator 0.4.8,默认设置,无尺寸调整
  2.   
  3. Photoshop CS5 with bicubic algorithm
  4.   
  5. imgscalr 4.2,ULTRA_QUALITY设置,无尺寸调整
  6.   
  7. Graphics2D(Java 8),提示提示VALUE_INTERPOLATION_BICUBIC,VALUE_RENDER_QUALITY,VALUE_ANTIALIAS_ON
  8.   

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

计算时间

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

  
      
  • 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
  •   

Here is the code used in this benchmark.

有趣的是 Thumbnailator也是最快的,平均时间为6.9秒,其次是 Java2D,7.2秒 imgscalr落后于26.2秒。这可能不公平,因为imgscalr设置为2560x1440,这似乎非常昂贵; ULTRA_QUALITY设置的平均值为11.1秒,更具竞争力。

答案 3 :(得分:5)

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

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

答案 4 :(得分:2)

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

在您链接到的PictureScaler课程中,在paintComponent方法中,它使用六种不同的方法调整图片大小。你有没有尝试过所有六个看哪个效果最好?

答案 5 :(得分:0)

经过一些令人沮丧的实验后,我找到了以下resize evaluation,并在我的项目中使用了多遍方法。

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

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

答案 6 :(得分: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();
    }
  }
}