sharp icon indicating copy to clipboard operation
sharp copied to clipboard

Weird artifacts when encoding PNG

Open chescos opened this issue 10 months ago • 6 comments

Possible bug

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

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

Are you using the latest version of sharp?

  • [x] 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 avatar Apr 10 '24 17:04 chescos

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.

lovell avatar Apr 10 '24 18:04 lovell

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 avatar Apr 10 '24 18:04 chescos

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

chescos avatar Apr 10 '24 19:04 chescos

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.

lovell avatar Apr 10 '24 20:04 lovell

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?

chescos avatar Apr 10 '24 22:04 chescos

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.

lovell avatar Apr 11 '24 07:04 lovell

I hope this information helped. Please feel free to re-open with more details if further assistance is required.

lovell avatar May 16 '24 12:05 lovell