如何获得高质量的缩略图

19

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

full scale image

这是相关源代码,其中 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 thumbnail

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

jpg thumbnail

使用imagemagick identify,我发现JPEG的质量只有75,这对于创建一个好看的缩略图来说完全不够。PNG对我来说也不好看。

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

$ 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?我想尝试更高的质量并比较结果。在ImageIO.write的JavaDoc中找不到任何内容。
  • 为什么我告诉Scalr我的最大尺寸是77x57,它输出一个77x58的图像?我认为这是为了保持比例,但那些是我的最大宽度和最大高度。宽度或高度可以小于等于这个值,但不能超过。

更新:通过网页搜索,我找到了一篇文章,介绍了如何调整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 thumbnail

JPEG质量75:

jpg thumbnail

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

jpg thumbnail quality 90

和文件大小:

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

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

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:

jpg thumb from scalr

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

jpg thumb from java-image-scaling

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


嗨@Riyad,感谢分享Scalr。我正在尝试它,但遇到了一些困难,你能看一下并给我一些提示吗? - stivlo
2
writeJpeg() 需要在 finally 块中调用 writer.dispose()output.close() 以释放这些资源! - quietmint
@user113215,谢谢你,我已经按照建议更新了示例代码。 - stivlo
感谢分享writeJpeg()方法!这是Scalr.resize的缺失拼图。 - 1813222
5个回答

1

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

java-image-scaling有一些不错的过滤器来帮助你进行微调(如果需要的话)。 话说,在imgscalr的v4.2中,我添加了新的ULTRA_QUALITY,这可能会让您更接近您想要的效果。

希望这有所帮助,但要注意,这是在一年之后才回复的。 很抱歉。


1

这并不是对您问题的完整回答,但是:

关于JPEG质量:

压缩质量可以使用ImageWriteParam进行设置,如此处所述。他们建议使用0|1的int值,但我认为实际上应该指定一个介于0.0和1.0之间的float值。

关于您的缩放维度问题:

Scalr主页得知:

注意:如果提供的宽度和高度违反了图像的比例(例如,尝试将800×600的图像调整为150×150的正方形),则库将首先查看图像的方向(横向/正方形或纵向),然后选择主要维度(横向或正方形使用宽度,纵向使用高度)重新计算正确的次要维度;忽略用户传入的违反比例的参数
在您的情况下,主要维度将是宽度77,因此您的高度限制57将被忽略。

谢谢Teabot +1,我会添加我的包装器来减少一个维度,这样我就可以适应我的空间了。 - stivlo
嗨@teabot,我也尝试了java-image-scaling,如果你有时间,能否看一下我问题底部的两个缩略图?如果你看细节,比如前后轮、油箱、座位下方和发动机,最后一张图片对我来说似乎更少有像素和更自然,你同意吗? - stivlo

0
如果您想要高质量的结果,那么请使用[RapidDecoder][1]库。使用方法如下:
导入rapid.decoder.BitmapDecoder; ... Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image) .scale(width, height) .useBuiltInDecoder(true) .decode(); 如果您想缩小50%以下并获得高质量结果,请不要忘记使用内置解码器。

0
请注意,如果您的图像具有alpha通道,则两个库都存在问题。我只是在谈论缩小图像,我还没有测试过放大它们。
java-image-scaling可能会在图像周围创建一个丑陋的边框,这取决于图像,看起来非常糟糕。我找不到避免这种情况的方法。
Scalr仅在(超)质量模式下存在问题。但是,可以轻松地以有效的方式使用它:双三次插值会在透明图像中留下伪影,因此您可能希望避免使用它。由于它是(超)质量图像的默认设置,并且scaleImageIncrementally()受保护,因此如果您想要质量(比双线性滤波高一点的分数看起来非常模糊),则必须对其进行子类化。

我找不到避免这种情况的方法。去掉 alpha 通道?据我所知,这将涉及在内存缓冲区中擦除所有第四个字节的问题。 - Stijn de Witt
如果我有透明图像,通常不想去掉 alpha 通道。我是指避免在生成的缩略图中出现不必要的伪影。 - Axel Dörfler
对不起,你当然是正确的。但是如果你正在生成JPG,那么通常可以去掉alpha通道 :) - Stijn de Witt

0

我已经运行了相同的测试,java-image-scaling在小于250像素的缩略图方面明显具有更好的结果。它还支持锐化滤镜,使结果更好。

由于Scalr的语法通常更简单,只需要一行代码,因此我保留了这两个库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接