How to get a good quality thumbnail
Asked Answered
F

5

19

I am trying to create a high quality thumbnail of this image, with Java and Scalr 3.2

full scale image

This is the relevant source code, where THUMB_WIDTH = 77 and THUMB_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));

If I use format = "png", here is the result:

png thumbnail

If I use format = "jpg", here is the result:

jpg thumbnail

With imagemagick identify I've found out that the JPEG is saved with a quality of 75 that is totally insufficient to create a good looking thumbnail. The PNG doesn't look good either to me.

Here is the output of identify of the original file and the two thumbnails:

$ 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

Questions

  • How to improve the quality of the generated thumbnail?
  • How to save a JPEG with a higher quality? I'd like to try with higher quality and compare the results. I couldn't find anything in the JavaDoc for ImageIO.write.
  • Why I tell Scalr that my maximum dimensions are 77x57 and it output an image 77x58? I think that is to maintain the proportion, but those are my maximum width and maximum height. Width or height could be less but not more.

UPDATE: With a web search I found an article about how to adjust JPEG image compression quality. I wrote my own method to save a BufferedImage setting the quality:

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

Here are the results. PNG:

png thumbnail

JPEG quality 75:

jpg thumbnail

JPEG quality 90 (the gravatars on stackoverflow are saved as JPEG quality 90):

jpg thumbnail quality 90

and the filesize:

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

UPDATE 2: test to compare Scalr with 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);
}

JPEG quality 90 generated with Scalr:

jpg thumb from scalr

JPEG quality 90 generated with java-image-scaling:

jpg thumb from java-image-scaling

I didn't receive any further feedback, so my personal conclusion is that java-image-scaling provides superior quality, and so it's the library that I choose.

Finespun answered 12/10, 2011 at 15:2 Comment(4)
Hi @Riyad, thanks for sharing Scalr. I'm trying it and I've some difficulties, can you take a look and give me a hint?Finespun
writeJpeg() needs to call writer.dispose() and output.close() in a finally block to release these resources!Archambault
@user113215, thank you, I've updated the sample code as suggested.Finespun
thank you for sharing the writeJpeg() method! This was the missing puzzle piece to Scalr.resize.Sanderling
R
1

This is not a complete answer to your question, but:

Regarding JPEG quality:

Compression quality can be set using a ImageWriteParam as described here. They suggest using an int value of 0|1 but I believe that you should actually specify a float value between 0.0 and 1.0.

Regarding your scaling dimension issues:

From the Scalr homepage:

NOTE: If a width and height are provided that violate the image’s proportions (e.g. attempt to resize an 800×600 image to a 150×150 square) the library will first look at the orientation of the image (landscape/square or portrait) and then select the primary dimension (landscape or square uses width, portrait uses height) to recalculate a correct secondary dimension; ignoring what was passed in by the user that was violating the proportions.

In your case the primary dimension will be a width of 77 and thus your height limit of 57 will be ignored.

Rodmann answered 12/10, 2011 at 15:9 Comment(2)
thank you teabot +1, I will add my wrapper then to reduce one of the dimensions, so I can fit my space.Finespun
Hi @teabot, I've also tried java-image-scaling, if you've time can you take a look to the two thumbs at the bottom of my question? If you look at details like the front and back wheel, the fuel tank, just beneath the seat and the engine, the last image seems to me less pixelated and natural, do you agree?Finespun
P
1

@Stivlo, I am sorry for not replying to this, I never got any notification from SO about the question.

java-image-scaling does have some nice filters to help with fine-tuning if you need it. That said, in v4.2 of imgscalr I added the new ULTRA_QUALITY that might get you closer to what you want.

I hope that helps, but realize this is being replied to almost a year after the fact unfortunately. Sorry about that.

Parietal answered 30/12, 2012 at 17:51 Comment(0)
K
0

I have run the same tests and java-image-scaling definitively have better results for thumbnails smaller than 250px. It also support sharp filtering, which make the results better.

I keep both libraries since the syntax of Scalr is often easier, with only one line.

Kalle answered 20/3, 2012 at 22:31 Comment(0)
B
0

Note that if your images have an alpha channel, both libraries are problematic. I'm only talking about shrinking images, I haven't tested enlarging them.

java-image-scaling may create an ugly border around the transparent edges depending on the image, and this looks very bad. I found no way to avoid this.

Scalr is only problematic using the (ultra) quality modes. It can easily be used in a way that works fine, though: bicubic interpolation leaves artifacts in transparent images, so you may want to avoid it. Since it's the default for (ultra) quality images, and scaleImageIncrementally() is protected you'd have to subclass it for this, though, if you want the quality (a fraction higher than 2 looks very blurry with bilinear filtering, though).

Benton answered 7/8, 2014 at 8:25 Comment(3)
"I found no way to avoid this." Get rid of the alpha channel? Afaict, that would be a matter of wiping all 4th bytes in the memory buffer.Unhallow
If I have transparent images, I usually don't want to get rid of the alpha channel. I'm talking about avoiding unwanted artifacts in the generated thumbnail.Quillan
I'm sorry, you are right of course. However if you are generating JPG then it's usually fine to get rid of alpha channel :)Unhallow
J
0

If you want high quality result, so use [RapidDecoder][1] library. It is simple as follow:

import rapid.decoder.BitmapDecoder; ... Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image) .scale(width, height) .useBuiltInDecoder(true) .decode(); Don't forget to use builtin decoder if you want to scale down less than 50% and a HQ result.

Jordon answered 25/8, 2016 at 11:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.