sharp icon indicating copy to clipboard operation
sharp copied to clipboard

Enhancement: use pipeline colourspace for composite operation, will allow CMYK compositing

Open Aminelahlou opened this issue 1 year ago • 9 comments

Feature request

I would like to composite a cmyk image onto another cmyk image

What are you trying to achieve?

I would like to composite cmyk images onto one cmyk image without converting to RGB the composited images

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

I did not find any because we are able to open cmyk images only since 0.32.1 so people were not able to composite images using cmyk images

What would you expect the API to look like?

Same api as composite method

What alternatives have you considered?

I considered fusing cmyk image buffers but it would be equivalent to rewriting composite function.

I also considered just concatenating images, but I did not find any api to concatenate images apart from composite which needs an image that we composite onto.

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

I have made a github repo with the input image (cmyk image with a Cyan value of 100 of 1000 pixel by 1000 pixel). What the repo does :

  1. Creating a label
  2. Modulating an image
  3. Compositing the label onto the modulated CMYK image (the label is just for information purpose so we don't care if it uses RGB and then is converted to cmyk)
  4. Creating a white image which would include all the images
  5. Compositing the modulated cmyk image with the label, onto the white cmyk image

When I execute this code, I have this error :

Input buffer contains unsupported image format

which means i cannot composite cmyk image onto another without losing information about the composited cmyk image since it needs to be converted into RGB unless there is a way I did not see.

I have made a github repo available here: https://github.com/Aminelahlou/sharpCmykTest (same as issue #3620) The image is input/test-sharp-input.tif The script to execute is test-sharp-composite.js

const sharp = require("sharp");
async function compositeFunc() {
  const labelInput = await sharp({
    text: {
      text: "random text",
      font: "Arial",
      width: 500,
      height: 400,
      channels: 4,
      background: { r: 255, g: 255, b: 255, alpha: 1 },
    },
  })
    .tiff()
    .toBuffer();
  const modulatedImage = sharp("./input/test-sharp-input.tif")
    .pipelineColourspace("cmyk")
    .modulate({ hue: 0, saturation: 1, brightness: 1 })
    .composite([
      {
        input: labelInput,
        width: 500,
        height: 400,
        channels: 4,
        top: 0,
        left: 0,
      },
    ])
    .toColourspace("cmyk");

  const whiteImage = await sharp({
    create: {
      width: 15000,
      height: 15000,
      channels: 4,
      background: { r: 255, g: 255, b: 255 },
      limitInputPixels: false,
    },
  })
    .toColorspace("cmyk")
    .toBuffer();

  await sharp(whiteImage)
    .pipelineColorspace("cmyk")
    .composite([
      {
        input: await modulatedImage.toBuffer().catch((err) => {
          console.log("file error 2: ", file);
          console.log(err);
        }),
        top: 0,
        left: 0,
      },
    ])
    .toColorspace("cmyk")
    .toFile("./output/test-sharp-composite-output.tif");
}
compositeFunc();

Aminelahlou avatar Apr 27 '23 18:04 Aminelahlou

I cloned https://github.com/Aminelahlou/sharpCmykTest then ran npm start and it successfully generated the output/test-sharp-output.tif file. Your comments above mention an "Input buffer contains unsupported image format" error but I don't see this. Am I missing something?

lovell avatar Apr 30 '23 09:04 lovell

I am sorry, i have not myself clear but the script to execute is test-sharp-composite.js, not npm start. Do you want me to make another repo with only information about this particular feature request?

Aminelahlou avatar Apr 30 '23 14:04 Aminelahlou

Ah, thanks, I missed that bit.

It looks like whiteImage contains raw pixel data.

https://github.com/Aminelahlou/sharpCmykTest/blob/f524c80aa8e0e2666eeaabafd79ee9e299db9872/test-sharp-composite#L30-L40

Raw pixel data needs to be described when passed as input, e.g. with width, but the current assumption is that 4 channel raw data will be RGBA so even if you add the required properties this won't work with raw CMYK anyway.

Perhaps you could add .jpeg() to the chain that produces whiteImage as the JPEG format supports CMYK images.

lovell avatar May 04 '23 08:05 lovell

I have added a .tiff() method to the file there is no more error like this:

Input buffer contains unsupported image format

However the final image has a Cyan color that was converted from CMJN=[100,0,0,0] to CMJN=[65,14,0,5]

Aminelahlou avatar May 09 '23 17:05 Aminelahlou

You might need to add a specific CMYK ICC profile to intermediary images via withMetadata to avoid the built-in CMYK profile being used for any implicit RGB to CMYK conversions.

lovell avatar May 10 '23 08:05 lovell

If I use withMetadata, will it keep my cmyk images as is, with no conversion to rgb whatsoever?

Aminelahlou avatar May 11 '23 14:05 Aminelahlou

I have updated my sharpCmyktest repo so we can setup a much more simpler test to better assess the problem I am trying to highlight : https://github.com/Aminelahlou/sharpCmykTest You just need to run npm start and it will run node test-sharp-composite-simple.js Expected behaviour: I should get a value of 100 of Cyan in the output file : test-sharp-composite-simple with a "random text" text Current behaviour: I get a value of C: 66 M:17 Y:0 K:1

If I delete the composite part, it works just fine with a value of Cyan of 100.

Aminelahlou avatar May 11 '23 16:05 Aminelahlou

@lovell Can I make a bounty on this issue?

Aminelahlou avatar May 30 '23 14:05 Aminelahlou

libvips provides a compositing_space option when compositing that sharp currently leaves with the default setting.

https://www.libvips.org/API/current/libvips-conversion.html#vips-composite

This is VIPS_INTERPRETATION_sRGB, VIPS_INTERPRETATION_B_W, VIPS_INTERPRETATION_RGB16, or VIPS_INTERPRETATION_GREY16 by default

As a future possible enhancement, I think it would make sense for this to be automagically set by sharp to use the value, if any, provided to pipelineColourspace.

lovell avatar Sep 27 '23 09:09 lovell