Git Product home page Git Product logo

Comments (4)

lovell avatar lovell commented on May 28, 2024

My best guess would be that this relates to (a lack of) gamut.

Does the following change help? It should ensure the input CMYK profile is respected when present, preventing the narrower, built-in CMYK profile being introduced and therefore allow the removal of pipelineColourspace(), which is hopefully a bit simpler (and matches your "Attempt 1" example).

--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -348,6 +348,7 @@ class PipelineWorker : public Napi::AsyncWorker {
         baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK
       ) {
         image = image.icc_transform(processingProfile, VImage::option()
+          ->set("embedded", TRUE)
           ->set("input_profile", "cmyk")
           ->set("intent", VIPS_INTENT_PERCEPTUAL));
       }

It's also possible that Photoshop's idea of inverting CMYK values is different to libvips. In addition sharp always tells the underlying lcms to use perceptual rendering intent, which might differ from Photoshop's treatment.

from sharp.

adriaanmeuris avatar adriaanmeuris commented on May 28, 2024

Thanks for the suggestion. I've did some tests without negating to see if the built-in CMYK profile is introduced by comparing with the output from vips.

Attached a 1000x1000 tiff file with all pixels set to 0 / 100 / 100 / 0 with embedded profile Coated Fogra39.

# Get source pixel value (output: 0 255 255 0)
vips getpoint fogra.tif 0 0

# Convert to U.S. Web Coated (SWOP) v2
vips icc_transform fogra.tif swop.tif swop.icc

# Get destination pixel valuex (output: 10 255 255 1)
vips getpoint swop.tif 0 0

The destination pixels match exactly those of Photoshop when converting to the SWOP profile using relative colorimetric intent and black point compensation turned off (= the defaults of vips icc_transform).

I did the same icc transform using sharp with every combination oftoColourspace and pipelineColourspace:

await sharp('fogra.tif').toColourspace('cmyk').pipelineColourspace('cmyk').withIccProfile('swop.icc').toFile('swop1.tif');
await sharp('fogra.tif').toColourspace('cmyk').withIccProfile('swop.icc').toFile('swop2.tif');
await sharp('fogra.tif').pipelineColourspace('cmyk').withIccProfile('swop.icc').toFile('swop3.tif');
await sharp('fogra.tif').withIccProfile('swop.icc').toFile('swop4.tif');

Here's the output pixel values:

Test Output
vips icc_transform fogra.tif swop.tif swop.icc && vips getpoint swop.tif 0 0 10 255 255 1
vips getpoint swop1.tif 0 0 25 255 255 4
vips getpoint swop2.tif 0 0 23 244 238 3
vips getpoint swop3.tif 0 0 24 251 247 3
vips getpoint swop4.tif 0 0 25 255 255 4

After applying your code change in pipeline.cc, I see no change in these output values so I suspect the built-in CMYK profile is still being introduced somehow.

Happy to test any other suggestion.

from sharp.

lovell avatar lovell commented on May 28, 2024

Thank you, this is useful info, and points to the difference being rendering intent:

$ vips icc_transform fogra.tif swop.tif swop.icc --intent=perceptual
$ vips getpoint swop.tif 0 0
25 255 255 4 

sharp uses perceptual intent to try to avoid too much visually-obvious gamut clipping, but this can come at the cost of saturation levels, which from these examples appears to adversely affect CMYK-to-CMYK transformations.

Maybe we should switch to always using relative intent when transforming CMYK images?

from sharp.

roeldl avatar roeldl commented on May 28, 2024

Thank you for taking a look at this. I have tested the initial bug and played around in pipeline.cc by moving the image.icc_transform which is triggered by the withIccProfile parameter to the beginning of the pipeline.

It seems that when the conversion happens before the negate that the right result is produced.

So in the case below, when withIccProfile is set on the sharp instance before the negate. I think the conversion should happen before the negate operation is executed.

await sharp('tiger-cmyk-fogra.tif')
  .toColourspace('cmyk')
  .pipelineColourspace('cmyk')
  .withIccProfile(swopPath)
  .negate()
  .toFile('tiger-cmyk-swop-inverted-attempt4.tif');

Place in the pipeline where the conversion to ICC happens:

if (!baton->withIccProfile.empty()) {

Negate works when moved to:

I hope this helps! Thanks again for your help.

from sharp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.