如何获得高质量的缩略图

我正在尝试使用Java和Scalr 3.2创建此图像的高质量缩略图

全尺寸图像

这是相关的源代码,其中THUMB_WIDTH = 77THUMB_HEIGHT = 57

 BufferedImage srcImg = ImageIO.read(new File(sourceFile)); BufferedImage dstImg = Scalr.resize(srcImg, Scalr.Method.QUALITY, THUMB_WIDTH, THUMB_HEIGHT); ImageIO.write(dstImg, format, new File(destFile)); 

如果我使用format = "png" ,结果如下:

png缩略图

如果我使用format = "jpg" ,结果如下:

jpg缩略图

通过imagemagick识别,我发现JPEG的质量保存为75,完全不足以创建漂亮的缩略图。 PNG对我来说也不好看。

以下是原始文件的标识输出和两个缩略图:

 $ identify 42486_1.jpg 42486_s1.jpg 42486_s1.png 42486_1.jpg JPEG 580x435 580x435+0+0 8-bit DirectClass 50.6KB 0.000u 0:00.000 42486_s1.jpg[1] JPEG 77x58 77x58+0+0 8-bit DirectClass 2.22KB 0.000u 0:00.000 42486_s1.png[2] PNG 77x58 77x58+0+0 8-bit DirectClass 12.2KB 0.000u 0:00.000 

问题

  • 如何提高生成的缩略图的质量?
  • 如何保存更高质量的JPEG? 我想尝试更高的质量并比较结果。 我在JavaDoc for ImageIO.write中找不到任何东西。
  • 为什么我告诉Scalr我的最大尺寸是77×57并输出图像77×58? 我认为这是保持比例,但那是我的最大宽度和最大高度。 宽度或高度可以更小但不是更多。

更新 :通过网络搜索,我找到了一篇关于如何调整JPEG图像压缩质量的文章 。 我编写了自己的方法来保存BufferedImage设置的质量:

 /** * 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 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(); } } 

结果如下。 PNG:

png缩略图

JPEG质量75:

jpg缩略图

JPEG质量90(stackoverflow上的gravatars保存为JPEG质量90):

jpg缩略图质量90

和文件大小:

 thumb90.jpg JPEG 77x58 77x58+0+0 8-bit DirectClass 6.89KB 0.000u 0:00.000 

更新2 :测试将Scalr与java-image-scaling进行比较。

 private void scaleAndSaveImageWithScalr(String sourceFile, String destFile, int width, int height) throws IOException { BufferedImage sourceImage = ImageIO.read(new File(sourceFile)); BufferedImage destImage = Scalr.resize(sourceImage, Scalr.Method.QUALITY, width, height); writeJpeg(destImage, destFile, JPEG_QUALITY); } private void scaleAndSaveImageWithJImage(String sourceFile, String destFile, int width, int height) throws IOException { BufferedImage sourceImage = ImageIO.read(new File(sourceFile)); ResampleOp resampleOp = new ResampleOp(width, height); resampleOp.setFilter(ResampleFilters.getLanczos3Filter()); resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Normal); BufferedImage destImage = resampleOp.filter(sourceImage, null); writeJpeg(destImage, destFile, JPEG_QUALITY); } 

使用Scalr生成的JPEG质量90:

来自scalr的jpg thumb

使用java-image-scaling生成的JPEG质量90:

来自java-image-scaling的jpg thumb

我没有收到任何进一步的反馈,所以我的个人结论是java-image-scaling提供了卓越的质量,因此它是我选择的库。

这不是您问题的完整答案,但是:

关于JPEG质量:

可以使用此处所述的ImageWriteParam设置压缩质量。 他们建议使用0 | 1的int值,但我相信你应该实际指定一个介于0.0和1.0之间的浮点值。

关于缩放维度问题:

来自Scalr主页 :

注意:如果提供的宽度和高度违反图像的比例(例如,尝试将800×600图像调整为150×150平方),库将首先查看图像的方向(横向/正方形或纵向)和然后选择主要尺寸(横向或方形使用宽度,纵向使用高度)重新计算正确的次要尺寸; 忽略违反比例的用户传入的内容。

在您的情况下,主要尺寸的宽度为77,因此您的高度限制为57将被忽略。

@Stivlo,我很抱歉没有回复这个问题,我从来没有收到任何关于这个问题的通知。

如果需要,java-image-scaling确实有一些很好的filter可以帮助进行微调。 也就是说,在imgscalr的v4.2中,我添加了新的ULTRA_QUALITY ,可以让你更接近你想要的。

我希望这有所帮助,但不幸的是,事实发生这种情况的回复差不多一年。 对于那个很抱歉。

我已经运行了相同的测试,并且java-image-scaling明确地对于小于250px的缩略图有更好的结果。 它还支持锐利过滤,使结果更好。

我保留了两个库,因为Scalr的语法通常更容易,只有一行。

请注意,如果您的图像具有Alpha通道,则两个库都存在问题。 我只谈论缩小的图像,我还没有测试扩大它们。

java-image-scaling可能会根据图像在透明边缘周围创建一个丑陋的边框,这看起来非常糟糕。 我发现没有办法避免这种情况。

Scalr只使用(超)质量模式有问题。 它可以很容易地以一种工作正常的方式使用:双三次插值会在透明图像中留下伪影,因此您可能希望避免它。 因为它是(超)质量图像的默认值,并且scaleImageIncrementally()受到保护,你必须为此子类化,但是,如果你想要质量(高于2的分数看起来非常模糊,但是使用双线性过滤)。

如果您想要高质量的结果,请使用[RapidDecoder] [1]库。 它很简单如下:

import rapid.decoder.BitmapDecoder; … Bitmap bitmap = BitmapDecoder.from(getResources(),R.drawable.image).scale(width,height).useBuiltInDecoder(true).decode(); 如果您希望缩小小于50%和HQ结果,请不要忘记使用内置解码器。