lovell / sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images. Uses the libvips library.
https://sharp.pixelplumbing.com
Apache License 2.0
29.29k stars 1.3k forks source link

Blurry image, when resizing from big size to small #1982

Closed Albertbol closed 4 years ago

Albertbol commented 4 years ago

Hey, First of all thanks for such a great library, i appreciate your time investment in this project for all of us!

Now to the question: I have high quality image 2980x3974 .jpg, i want to make it smaller to 200px height and i set auto width, but i get very blurry image back, cant figure out what settings i need to pass to have good quality sharp image back. Code snippet:

     await sharp(tmpFilePath)
      .resize(null, 200, {
        kernel: sharp.kernel.kernel
      })
      .jpeg({
        quality: 100,
        chromaSubsampling: '4:4:4',
        kernel: sharp.kernel.kernel
      })
      .sharpen()
      .rotate()
      .toFile(tmp200x200Path)

I tried to use sharpen only in one place, in two places, all possible options, tried kernel: 'cubic', but allways same result, with very blurry image.

lovell commented 4 years ago

Are you able to provide a sample image that helps explain the question?

Albertbol commented 4 years ago

Sure Original image: original Resized: resized Of course i understand that its a huge downsize, i made to 600px height also just to show blurry effect, its better quality than 200px height but still far from original image quality: 600height

lovell commented 4 years ago

Thanks, to help me understand your expectations, are you able to produce an image at the same dimensions using a different application to demonstrate how sharp compares?

The default resizing kernel used for downsampling by sharp (and libvips) is Lanczos 3, which usually produces the best results for the vast majority of cases.

In your code sample, I note you've set kernel to the undefined sharp.kernel.kernel so it will default to "lanczos3", plus you're passing an ignored kernel property to jpeg().

JPEG is lossy, even at "100% quality", so perhaps try the lossless PNG format for output.

You might want also to explore changing the fastShrinkOnLoad property on resize().

https://sharp.pixelplumbing.com/en/stable/api-resize/#resize https://sharp.pixelplumbing.com/en/stable/api-output/#jpeg

Albertbol commented 4 years ago

1) fastShrinkOnLoad: true or fastShrinkOnLoad: false didnt change the output quality. 2) Can i ask how to convert image?

 await sharp(tmpFilePath)
      .resize(null, 200, {
        kernel: sharp.kernel.kernel
      })
      .toFormat('png')
      .rotate()
      .toFile(tmp200x200Path)

Still saving as a jpeg...

lovell commented 4 years ago

Your code should produce PNG output (toFormat takes precedence over any filename extension - this is a tested scenario e.g. https://github.com/lovell/sharp/blob/master/test/unit/io.js#L526).

Were you able to produce an image at the same dimensions using a different application to demonstrate how sharp compares?

jahanford commented 4 years ago

I'm also having some issues with blurry downscale resizing.

Original: surface (orig)

Sharp (200px width, auto height downscale) surface (sharp 200)

Paint (200px width downscale) surface (paint 200)

For this example I took the advice of using fastShrinkOnLoad false

return Promise.resolve(sharpImageCopy
    .resize(resize.width, resize.height, {
        fastShrinkOnLoad: false
    })
    .jpeg({
        quality: 100
    })
    .toBuffer()
});
jahanford commented 4 years ago

After looking into this further, this may be an underlying vips issue as resize looks to be similar using vips-cli lanczos3.

vips resize image.jpg image-vips.jpg 0.1041666666666667‬ --kernel lanczos3

image

lovell commented 4 years ago

I think MS Paint uses GDI+ under the hood so will be either bilinear or bicubic interpolation:

https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-using-interpolation-mode-to-control-image-quality-during-scaling-use

kleisauke commented 4 years ago

According to https://stackoverflow.com/a/48855445/10952119 it seems MS Paint also sharpens the image with a convolution kernel after resizing. I tried this:

width=$(vipsheader -f width owl-black.jpg)
height=$(vipsheader -f height owl-black.jpg)
# size=$((width > height ? width : height))
# factor=$(bc <<< "scale=10; 200 / $size")
# vips resize owl-black.jpg x.v $factor --kernel cubic
hscale=$(bc <<< "scale=10; 200 / $width")
vscale=$(bc <<< "scale=10; 112 / $height")
vips resize owl-black.jpg x.v $hscale --vscale $vscale --kernel cubic

cat > mask.con <<EOF
3 3 1 0
0.0 -0.125 0.0
-0.125 1.5 -0.125
0.0 -0.125 0.0
EOF
vips conv x.v x.jpg mask.con --precision float

(I used bicubic interpolation instead of bilinear)

Output: x vips 8.8.4

x2 vips 8.9.0 (with this patch applied)

The patched vips image looks almost identical to the MS Paint one (try to switch the images in your browser at 500% zoom level). The owl's eye only looks more saturated in MS Paint, but I'm not sure if that's the "correct output".

jahanford commented 4 years ago

@kleisauke @lovell unfortunately I am only just starting down the rabbit hole of image interpolation, I apologise for my lack or knowledge in this space. Is the performance of lanczos3 treated as undesired in this case or is it a trade off for some other criteria?

lovell commented 4 years ago

The use of bicubic interpolation can result in greater overshoot/halo/moire effects compared with Lanczos, but it depends on the source image.

I'll close this question as the ability to specify interpolator and convolution kernels are already available and supported in both libvips and sharp.

Andrew-web-coder commented 3 years ago

@lovell Please, provide a working solution to this issue. The homepage has tons of examples, and it is great, but isn't it ridiculous that the tool named "sharp" does not have any sample/info about how to make images sharp. I am creating thumbnails from big images and they are too blurry, I tried to use sharpen with random numbers (no idea what they are, because there is no demo/examples given), but I am not satisfied with the outcome.

lovell commented 3 years ago

@Andrew-web-coder If there's a specific image you'd like help with, please open a new issue with the code you are using and ensuring you detail all of the approaches you've tried so far, including the useful (and very much not "ridiculous") suggestions in this issue to try different resizing kernels and applying a convolution kernel.

Andrew-web-coder commented 3 years ago

I know, documentation is hard, I really do. I just wish there were more demos, more explanation about what those kernels are, how each parameter affects the result, etc. I wish you good luck and energy to continue working on your amazing tool. Thanks.

Andrew-web-coder commented 3 years ago

FYI, I ended up not resizing and not sharping my images using this tool, because browsers do much better job. So I simply created large images and let browser to do the rest.

suresh-jbt commented 1 year ago

@Albertbol use this kernel: kernel: sharp.kernel.lanczos3

maor-benami commented 1 year ago

I also experience the same issue.

Source image (retina screen capture with selenium):

image

After resizing (png):

image
imanpalsingh commented 1 year ago

This helped me

.convolve({
        width: 3,
        height: 3,
        kernel: [0.0, -0.125, 0.0, -0.125, 1.5, -0.125, 0.0, -0.125, 0.0],
      })
duongdinh98 commented 9 months ago

Using kernel: sharp.kernel.lanczos3 look good to me in my case 800*800