【问题标题】:Image resize quality (Java) [closed]图像调整大小质量(Java)[关闭]
【发布时间】:2010-12-10 03:43:27
【问题描述】:

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

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

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

【问题讨论】:

    标签: java image resize jpeg photo


    【解决方案1】:

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

    在您链接到的PictureScaler 类中,在paintComponent 方法中,它使用六种不同的方法来调整图像大小。您是否尝试了所有六种方法,看看哪一种效果最好?

    【讨论】:

    • 我要补充一点,在代码中,提问者有一个“高质量”模式,涉及在几遍中调整图像大小。这似乎是个坏主意,原因与您不想重复压缩和解压缩声音样本的原因大致相同。图像质量实际上会更差,而不是更好。
    • 嗨,实际上我还没有尝试所有我可能应该尝试的六个。尽管我使用的是双三次重采样,正如您所说,“应该”是最好的。我也没有使用“高质量”模式,因为它在那种情况下不起作用,Java 成像 API 似乎容易出现死锁!可能我的代码有问题,但不确定是什么。
    【解决方案2】:

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

    【讨论】:

    • 我希望避免外部呼叫,但我认为您可能是对的。 Java 似乎还没有完全正确地处理图像!
    • 这也是我的结论! Java 图像 API 简直糟透了!我可以解决缓慢的问题,但糟糕的图像质量是不可接受的!我确实希望相关 api 与 JavaFX 2.0 一起获得一些急需的 tlc。
    • 你显然没有遇到java-image-scaling
    • 尝试了 java-image-scaling 库,图像比我的自定义方法清晰得多。感谢您的链接
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      我已经使用这些方法进行了相当多的测试,而增量缩放以及坚持支持良好的图像类型是关键 - 我看到 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。

      【讨论】:

      • 您好,Riyad,感谢您为构建它付出的努力。很棒,我刚开始使用它。当我尝试仅将图像调整为特定宽度和高度时,我遇到了质量问题。我有一个 240X320 的图像,我必须将其调整为 50X75 和 120X180 。我使用以下代码分别尝试了两者(我使用的是 imgscalr 4.2)。
      • image=resize(image,Method.ULTRA_QUALITY, 50,75, OP_ANTIALIAS, OP_BRIGHTER); saveImage(image, ImageFormatTypes.JPEG, DESTINATION + RESIZED_IMAGE + "."+ImageFormatTypes.JPEG);我有两个问题。与我用来手动调整图像大小的其他内部工具相比,#1 的质量非常低。 #2。宽度从 50 增加到 56,从 120 增加到 135,这很奇怪。你知道为什么会发生这些吗?感谢您的帮助。
      • @WowBow 啊!是的,我想我知道发生了什么......我讨厌 imgscalr 还没有从开发人员中抽象出来,但它对原始像素值进行操作——当你用 ImageIO 写回图像时,它使用的是 DEFAULT JPG 编码器设置,我相信它们设置为 75% 的质量——你必须在谷歌上搜索“java ImageIO JPEG 质量”,例如如何将其设置为 95% 的代码。测试是否是这种情况的“便宜且简单”的方法是将图像写成 PNG(losssles 格式)——如果它看起来“更好”,那么就是这样。
      • @WowBow 还有一个建议,尝试删除 AA 和 Brightness OP,以防他们对图像做了一些奇怪的事情,然后只写出缩放后的图像,看看它的外观。
      • 谢谢。我会试试看。
      【解决方案5】:

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

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

      【讨论】:

      • 结合更改压缩级别 (blog.carsoncheng.ca/2011/02/…) 测试了 org.imgscalr.Scalr 和 com.mortennobel.imagescaling,但均未奏效。 thumbnailator很棒,至少在质量方面。谢谢!
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        目前两个最流行的专门用于在 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.

        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 的输出要柔和得多。对于图标/文本等,您需要更清晰的输出,对于图片,您可能需要更柔和的输出。

        计算时间

        这是使用 tool 和 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
        • 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 秒。

        【讨论】:

        • 性能测量很困难(我在this answer 中写了一些关于此的内容)。尽管如此,为这个和另一个答案加 1
        • @Marco13 是的,但我认为 a 有足够大且多样化的样本量,至少可以捕捉到趋势。
        • 我自己完成了测试并观察到与您类似的模式(Thumbnailator 提供了最快的性能)。我建议您将“imgscalr”行编写两次(一次用于 ULTRA_QUALITY,一次用于 QUALITY),以便更容易快速比较基准。
        猜你喜欢
        • 2014-02-17
        • 2021-02-15
        • 2012-12-16
        • 1970-01-01
        • 2013-02-15
        • 2011-01-24
        • 2020-08-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多