代码之家  ›  专栏  ›  技术社区  ›  user7094

图像调整质量(Java)[关闭]

  •  15
  • user7094  · 技术社区  · 16 年前

    我有一个开源应用程序,可以将照片上传到脸书。为了节省带宽,照片在上传前会自动调整大小(脸书规定了最大大小限制)。一些人抱怨照片质量,事实上,你可以看到其中的区别(见 this issue 对于一些演示图像)。

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

    你可以看到我当前的代码 here this page ).

    7 回复  |  直到 16 年前
        1
  •  17
  •   georgeawg    15 年前

    Phil,我不知道你最终选择了哪种解决方案,但如果你:

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

    我用这些方法做了相当多的测试,增量缩放以及坚持支持良好的图像类型是关键——我看到亚历山大提到他仍然没有好运,这很令人沮丧。

    我释放了 imgscalr library (Apache 2)大约6个月前,在阅读了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,如果你好奇的话。

        2
  •  13
  •   Community Mohan Dere    9 年前

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

    此外,还有JDK与Java的结合方式 Graphics2D ( see this question on how to do it )这是臭名昭著的 bad results especially with downscaling .还有一个 Java interface to ImageMagick 这里将省略它,因为它需要外部工具。

    视觉质量

    以下是调整大小/缩小比例的结果比较 580x852 145x213 。作为参考,使用Photoshop CS5“保存为网络”调整大小。 注意:结果是libs刚刚复制在一起的1:1。缩放不使用任何过滤,只使用简单的最近邻算法。 Here you can find the original image.

    comparison

    1. 缩略图0.4.8,默认设置,无尺寸调整
    2. 使用双三次算法的Photoshop CS5
    3. imgscalr 4.2,带ULTRA_QUALITY设置,无尺寸调整
    4. 图形2D(Java 8),带有渲染提示VALUEINTERPOLATION_BICUBIC、VALUE_render_QUALITY、VALUE_ANTIALIAS_ON

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

    计算时间

    以下是使用此方法的非科学基准 tool 以及114幅图像,其尺寸约为 96x96 最多 2560x1440 将其视为425%的图像创建:100%、150%、200%、300%和400%的缩放版本(即114*5的缩放操作)。所有库都使用与质量比较中相同的设置(因此质量尽可能高)。时代只是在扩大,而不是整个过程。在配备8GB内存和5次运行的i5-2520M上完成。

    • 拇指矫正器 :7003.0毫秒|6581.3毫秒|6019.1毫秒|6375.3毫秒|8700.3毫秒
    • imgscalr :25218.5毫秒|25786.6毫秒|25095.7毫秒|25790.4毫秒|29296.3毫秒
    • 图形2D :7387.6毫秒|7177.0毫秒|7048.2毫秒|7132.3毫秒|7510.3毫秒

    Here is the code used in this benchmark.

    有趣的是 拇指爬行器也是最快的,平均时间为6.9秒 紧随其后 Java2D,7.2秒 离开 imgscalr落后26.2秒 。这可能不公平,因为imgscalr设置为 ULTRA_QUALITY 这似乎非常昂贵;随着 QUALITY 将其平均设置为更具竞争力的11.1秒。

        3
  •  12
  •   bobndrew Daniel Hiller    5 年前

    我什么都试过了,包括那些把戏 here

        4
  •  5
  •   SAN    14 年前

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

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

        5
  •  2
  •   Kip    16 年前

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

    PictureScaler 您链接到的类,在 paintComponent 方法,它使用六种不同的方法来调整图像的大小。你试过这六种方法,看看哪一种效果最好吗?

        6
  •  0
  •   Mladen Adamovic    13 年前

    我希望在保持纵横比的前提下,实现最高质量的尺寸调整。 尝试了一些东西,读了几篇文章。浪费了两天时间,最后我用纯Java方法得到了最好的结果(还尝试了ImageMagick和Java图像缩放库):

    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();
        }
      }
    }
    
        7
  •  0
  •   bobndrew Daniel Hiller    5 年前

    经过几次令人沮丧的实验,我发现了以下内容 resize evaluation ,并在我的项目中采用了多通道方法。

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

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

    推荐文章