Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

png resize artefacts related to premultiplication rounding? #4051

Open
dannerei opened this issue Apr 2, 2024 · 10 comments
Open

png resize artefacts related to premultiplication rounding? #4051

dannerei opened this issue Apr 2, 2024 · 10 comments
Labels

Comments

@dannerei
Copy link

dannerei commented Apr 2, 2024

What are you trying to achieve?

We develop a product that relies heavily on image processing, and in particular on image resizing. We were previously using ImageMagick for this but wanted to transition to Sharp. Using Sharp 0.31.3 (or earlier), resizing (downscaling) png images with transparency works as expected (i.e the results using Sharp are almost indistinguishable from the results using ImageMagick), but using Sharp >= 0.32.0 downscaled pngs get weird color artefacts at the cross over from opaque to transparent. Our first thought was that libvips had changed something between 8.13 and 8.14, but using libvips directly proved that theory wrong. Hence, our best guess is that the change to "Prefer integer (un)premultiply for faster resizing of RGBA images", introduced in Sharp 0.32.0, is to blame for what we are seeing. If so, would it be possible to add a flag to opt out of using the integer premultiply when resizing?

When you searched for similar issues, what did you find that might be related?

#3658

Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this question

sharp("path to image").resize(20, 20)

Please provide sample image(s) that help explain this question

These images are very small in size and zooming will be required to notice the artefacts I'm referring to. Larger images can be provided if needed.

Original (100x100)
sample
Resized using Sharp 0.31.3 (20x20)
sample-resized-using-sharp-0 31 3
Resized using Sharp 0.33.3 (20x20). Notice the discoloring of the top opaque pixel row.
sample-resized-using-sharp-0 33 3

@lovell
Copy link
Owner

lovell commented Apr 4, 2024

Hi, you can use pipelineColourspace to force the processing colourspace (and therefore force a bitdepth).

For example, if you want floating-point RGB, try scrgb:

sharp("path to image")
  .pipelineColourspace('scrgb')
  .resize(20, 20)
  ...

@dannerei
Copy link
Author

dannerei commented Apr 5, 2024

That seemed to do the trick. Thanks a lot!
One thing though. According to the documentation that feature is experimental, so can we rely on it?

@lovell
Copy link
Owner

lovell commented Apr 5, 2024

that feature is experimental, so can we rely on it?

Experimental insofar as there are probably still some untested code paths, with the aim to add more test cases as bugs are found and fixed. I don't expect to remove this feature.

@dannerei
Copy link
Author

dannerei commented Apr 9, 2024

Hi again, and an update on this:

We have now run a lot of tests using the pipelineColourspace('scrgb') setting, and although the originally reported rounding artefacts are gone, we have noticed that the resulting images look sharpened in a way that is not to our liking. Especially in transitional areas between bright and dark, the edges get unrealistically sharpened. Therefore I ask again, would it be possible to add a flag to opt out of using the integer premultiply when resizing (the 0.31.3 behaviour)?

@lovell
Copy link
Owner

lovell commented Apr 9, 2024

we have noticed that the resulting images look sharpened in a way that is not to our liking

Please can you provide sample images and minimal code that allows someone else to reproduce. Please also include more information about expected vs actual output.

@lovell
Copy link
Owner

lovell commented Apr 9, 2024

Aside: I've removed the "experimental" status of pipelineColourspace via commit f67228e

@dannerei
Copy link
Author

dannerei commented Apr 10, 2024

Here are a few samples using sharp 0.33.3 with and without pipelineColourspace. We expected the outputs to be very similar, but as can be seen there are major differences in the actual results.

Resize sample 1:

Original image:
sample1_orig

Result using sharp("path to image").resize(240, 240)
sample1

Result using sharp("path to image").pipelineColourspace('scrgb').resize(240, 240)
sample1_scrgb

Resize sample 2:

Original image:
sample2_orig

Result using sharp("path to image").resize(240, 240)
sample2

Result using sharp("path to image").pipelineColourspace('scrgb').resize(240, 240)
sample2_scrgb

@lovell
Copy link
Owner

lovell commented Apr 10, 2024

Thanks for the examples, my best guess would be that the use of wide-gamut, linear scRGB is producing more accurate luminance levels in the output compared with non-linear sRGB.

If you would prefer less accurate luminance, perhaps try experimenting with gamma levels e.g. in=2.2, out=1.0.

@lovell
Copy link
Owner

lovell commented Apr 29, 2024

@dannerei Were you able to make any progress with this?

@dannerei
Copy link
Author

Unfortunately we were not able to reach satisfactory results in a consistent way using the proposed solution, so for now we're stuck with ImageMagick. I still hope that we can make the transition to sharp sometime in the future though, since it offers superior performance and a very pleasant API to work with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants