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

Weird artifacts when encoding PNG #4063

Open
3 tasks done
chescos opened this issue Apr 10, 2024 · 6 comments
Open
3 tasks done

Weird artifacts when encoding PNG #4063

chescos opened this issue Apr 10, 2024 · 6 comments
Labels

Comments

@chescos
Copy link

chescos commented Apr 10, 2024

Possible bug

Is this a possible bug in a feature of sharp, unrelated to installation?

  • Running npm install sharp completes without error.
  • Running node -e "require('sharp')" completes without error.

Are you using the latest version of sharp?

  • I am using the latest version of sharp as reported by npm view sharp dist-tags.latest.

What is the output of running npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp?

  System:
    OS: Linux 5.15 Ubuntu 22.04.4 LTS 22.04.4 LTS (Jammy Jellyfish)
    CPU: (12) x64 AMD Ryzen 5 3600 6-Core Processor
    Memory: 14.05 GB / 31.28 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 20.5.1 - /usr/bin/node
    npm: 9.8.0 - /usr/bin/npm
  npmPackages:
    sharp: ^0.33.3 => 0.33.3 

What are the steps to reproduce?

Encode the below file as a PNG with quality 85:

Input

It will result in the following file:

Output

As you can see, there are some weird triangle artifacts at the bottom. Increasing the quality to 86 seems to completely remove those artifacts.

Here's a JS snippet that just does that:

const axios = require('axios');
const sharp = require('sharp');
const fs = require('fs');

(async () => {
  // Get the clear-text URL.
  const url = 'https://cdn.csgoskins.gg/public/images/floats/v2/p90-death-grip/0.png';

  const response = await axios.get(url, {
    responseType: 'arraybuffer',
  });

  const originalFileBuffer = Buffer.from(response.data, 'utf8');

  const newFileBuffer = await sharp(originalFileBuffer)
    .toFormat('png', {
      quality: 85,
    })
    .toBuffer();

  fs.writeFileSync('output.png', newFileBuffer);
})();
@chescos chescos added the triage label Apr 10, 2024
@lovell lovell added question and removed triage labels Apr 10, 2024
@lovell
Copy link
Owner

lovell commented Apr 10, 2024

https://sharp.pixelplumbing.com/api-output#png

[options.quality] use the lowest number of colours needed to achieve given quality, sets palette to true

[options.palette] quantise to a palette-based image with alpha transparency support

When quality is set, palette is also selected and therefore you have chosen to use a lossy, quantised output.

@chescos
Copy link
Author

chescos commented Apr 10, 2024

Ah, I see. I understand that it's a lossy output, but the output from my example is still very unexpected for me. The background color is a single color, and it just adds a completely different color but just in some areas. Is this the desired/expected functionality?

@chescos
Copy link
Author

chescos commented Apr 10, 2024

Is there any way to prevent this from happening while still setting a quality?

@lovell
Copy link
Owner

lovell commented Apr 10, 2024

The prebuilt binaries provided by sharp include the BSD-licenced libimagequant v2.4.1 for generating and mapping image palettes.

You may have some luck building your own libvips from source compiled with support for an alternative quantisation library such as a more recent GPL-licenced libimagequant or quantizr, but this is left as an exercise for the reader.

@chescos
Copy link
Author

chescos commented Apr 10, 2024

Thanks! I ended up setting dither to 0.0, which seems to fix the issue in all instances I tested. To your knowledge, should this reliably fix the issue?

@lovell
Copy link
Owner

lovell commented Apr 11, 2024

Ah yes, this does look like dithering gone a bit wrong, so altering the dither level until you find a value that works for the type of input you'll be processing is definitely a good idea.

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