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.27k stars 1.3k forks source link

1 bit dithered PNG without palette #3322

Open pepijndevos opened 2 years ago

pepijndevos commented 2 years ago

Question about an existing feature

What are you trying to achieve?

I am trying to make an 1 bit per pixel PNG image, but I want the colors to be pure black and white. When setting colours to 2, it uses a palette that may contain grayscales, which looks really bad when tiled.

I tried manually setting only options.pngBitdepth, but then no dithering occurs.

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

https://github.com/lovell/sharp/issues/2855 https://github.com/lovell/sharp/issues/2828

Some issues about getting dithered images, or 1 bit images, but everyone seems fine with a potentially gray palette.

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

extracted from https://github.com/maptiler/tileserver-gl/blob/master/src/serve_rendered.js but modified to output 1 bit dithered black and white.

          const image = sharp(data, {
            raw: {
              width: params.width * scale,
              height: params.height * scale,
              channels: 4
            }
          });
          image.toColourspace('b-w');
          image.removeAlpha();

          image.png({ adaptiveFiltering: false, dither: 1.0, colours: 2});
          image.toBuffer((err, buffer, info)

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

This is what the tiles look like with this option

image

lovell commented 2 years ago

It sounds like you want to specify a custom palette when quantising a PNG image, which is currently unsupported by libvips.

Perhaps start a new libvips discussion to see what others think - it would require API changes to the internals of quantise.c.

pepijndevos commented 2 years ago

That would be one way to do it, but should it not be possible to have no palette at all? Just N bit grayscale where 00... is black and 11... is white. Or is low bit depth PNG always paletted?

lovell commented 2 years ago

Dithering will only take place when using a palette.

If you're OK with the use of basic thresholding then the following should already do what you need.

  .toColourspace('b-w')
  .png({ colours: 2, palette: false })
  ...

However I've just spotted the value of colours (used to determine bitdepth) is currently only set when palette is true. Commit https://github.com/lovell/sharp/commit/3a44748f493f9c99061d30577df5fdd222b881e0 fixes this and adds a test that would previously have failed. This will be in v0.31.0.

pepijndevos commented 2 years ago

However I've just spotted the value of colours (used to determine bitdepth) is currently only set when palette is true. Commit https://github.com/lovell/sharp/commit/3a44748f493f9c99061d30577df5fdd222b881e0 fixes this and adds a test that would previously have failed. This will be in v0.31.0.

Thanks! Yes I experimented with these options and it didn't seem to work.

Dithering will only take place when using a palette.

Is that sharp, vips, or PNG limitation?I can't immediately see an obvious reason for this, but it mirrors my observations.

lovell commented 2 years ago

libvips currently provides support for dithering only when quantising, and provides support for quantising only when generating a palette-based image.

pepijndevos commented 2 years ago

Thanks, I'll start a vips discussion

Edit: https://github.com/libvips/libvips/discussions/3008

lovell commented 2 years ago

Upstream enhancement at https://github.com/libvips/libvips/issues/3010