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.23k stars 1.29k forks source link

TIFF output, bitdepth of 1 with LZW compression, should ignore predictor #3486

Open MosheL opened 1 year ago

MosheL commented 1 year ago

Possible bug

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

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

System: OS: Windows 10 10.0.22621 CPU: (6) x64 Intel(R) Core(TM) i5-8600K CPU @ 3.60GHz Memory: 20.28 GB / 63.95 GB Binaries: Node: 19.2.0 - C:\Program Files\nodejs\node.EXE npm: 9.1.3 - C:\Program Files\nodejs\npm.CMD

(tried also node 16.x and npm 8.x)

What are the steps to reproduce?

image.toColorspace('b-w').toFormat('tiff', { bitdepth: 1 }) // works, exports a 1-bit tiff file
image.toColorspace('b-w').toFormat('tiff', { bitdepth: 1, compression: 'lzw' }) // works, but exports a 24-bit file.

What is the expected behaviour?

the second row will export a 1-bit (or 2 or 4) bit TIFF file.

Thanks for all ! I like the package and it works good and fast.

lovell commented 1 year ago

Hi, I had to add predictor: 'none' to the second example to prevent the following error:

Horizontal differencing "Predictor" not supported with 1-bit samples

await sharp(input)
  .toColorspace("b-w")
  .toFormat("tiff", { bitdepth: 1 })
  .toFile("out1.tiff");

await sharp(input)
  .toColorspace("b-w")
  .toFormat("tiff", { bitdepth: 1, compression: "lzw", predictor: "none" })
  .toFile("out2.tiff");

Then, with this input, the output was as expected (latest version, using Linux):

$ ls *.tiff
 759008 out1.tiff
  77446 out2.tiff

$ tiffinfo out1.tiff 
TIFF Directory at offset 0x758734 (b93ce)
  Image Width: 2725 Image Length: 2225
  Resolution: 25.4, 25.4 pixels/inch
  Bits/Sample: 1
  Sample Format: unsigned integer
  Compression Scheme: None
  Photometric Interpretation: min-is-black
  Orientation: row 0 top, col 0 lhs
  Samples/Pixel: 1
  Rows/Strip: 256
  Planar Configuration: single image plane

$ tiffinfo out2.tiff 
TIFF Directory at offset 0x77172 (12d74)
  Image Width: 2725 Image Length: 2225
  Resolution: 25.4, 25.4 pixels/inch
  Bits/Sample: 1
  Sample Format: unsigned integer
  Compression Scheme: LZW
  Photometric Interpretation: min-is-black
  Orientation: row 0 top, col 0 lhs
  Samples/Pixel: 1
  Rows/Strip: 256
  Planar Configuration: single image plane

If you're still having problems, please provide complete, standalone code with input image that allows someone else to reproduce.

MosheL commented 1 year ago

Thanks for response.

For me, you solve the problem. Thanks. maybe this need to be documented somewhere for the next developer.

lovell commented 1 year ago

The error message is relatively self-explanatory, but this might be something we should address upstream in libvips so it ignores the predictor for 1 bpp images.

MosheL commented 1 year ago

there is no error message. the code in the example is working byt responded with a 24-bit. an error can be good.

thanks.

lovell commented 1 year ago

When specifying { bitdepth: 1, compression: 'lzw' } I always receive an error message plus a partial output that might look like 24-bit but has been truncated.

If you don't see the error message, please can you provide a sample image and complete, standalone code that includes appropriate error handling, which might allow someone else to reproduce.

MosheL commented 1 year ago

You are right, but not always.

When I am using a image without alpha channel, it working as you descibe in your comment. but if not, it not working as my bug is described.

I was used sharp-bmp here. sharp-bmp is working by exporting a 4-channel picture. When I was used a 1-bpp PNG image it was works (or more, but without Alpha channel).

here is a working example code, as I added .removeAlpha() before saving.

const sharp = require("sharp")
const fs = require("fs/promises")
const bmp = require('sharp-bmp');

async function a() {
    const buffer = await fs.readFile('./1.bmp');
    var bitmap = bmp.decode(buffer);

    //image = await sharp(buffer);
    image = await sharp(bitmap.data, {
        raw: {
            width: bitmap.width,
            height: bitmap.height,
            channels: 4,
        },
    })
    ///image = await sharp(buffer);
    const md = await image.metadata();
    bpp = md.paletteBitDepth || 4;
    console.log(bpp);

    await image.toColorspace('b-w').removeAlpha().toFormat('tiff', { bitdepth: 1, compression: "lzw", predictor: "none" })
        .toFile(`./1.tiff`);

    image = await sharp(bitmap.data, {
        raw: {
            width: bitmap.width,
            height: bitmap.height,
            channels: 4,
        },
    })

    await image.toColorspace('b-w').toFormat('tiff', { bitdepth: 1 })
        .toFile(`./2.tiff`);
}
a()