Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose setting ICC profile via withMetadata #218

Closed
keithpaton opened this issue May 12, 2015 · 35 comments
Closed

Expose setting ICC profile via withMetadata #218

keithpaton opened this issue May 12, 2015 · 35 comments

Comments

@keithpaton
Copy link

When I resize a CMYK TIFF, and output to TIFF the output file is RGB not CMYK.

Do I need to configure the output somehow to enable CMYK output?

Here is the code I am using:

var outputImage = sharp(source)
                .resize(outputSize.w,outputSize.h)
                .sharpen()
                .withMetadata()
                .interpolateWith(sharp.interpolator.bicubic)
                .toFile(outputFile,function(err) {
                    ...
                });
@lovell
Copy link
Owner

lovell commented May 12, 2015

Hi Keith, the most common use case of sharp is generating images for the web so the current behaviour is to always convert images to device-independent, non-linear sRGB colour space. Using withMetadata will also add the latest web-friendly v2 sRGB ICC profile.

It sounds like you're hoping to use it for a different purpose, so this sounds like an excellent enhancement to make.

Would an extension to withMetadata to allow it to accept a named profile help, something like withMetadata(sharp.profile.cmyk)? If the profile is not provided, it would default to sharp.profile.srgb to maintain the existing behaviour. This would mean the only way to get CMYK images out would be to explicitly request them; there would be no support for "pass-through" of the input profile.

@keithpaton
Copy link
Author

Hi Lovell,

That approach sounds ideal. Especially since it preserves your existing standard behaviour.

I am working with images for print applications, where the source and destination are CMYK and chose sharp because it can handle TIFF files. I need to handle CMYK JPEG input/output too, probably not as often as TIFF.

As part of the process, I am also generating RGB thumbnails and other images for use in mobile & web applications from the source TIFF and that part works perfectly, so I would love to be able to avoid having to use a different node package (or something more "low level") alongside to create the print/CMYK output.

If you were up for considering adding this enhancement, that would be fantastic. :-)

@lovell
Copy link
Owner

lovell commented May 13, 2015

Thanks for confirming Keith. Perhaps a separate toColourspace/toColorspace method would be more appropriate here, to support things like profile-less CMYK JPEG images. This could then be extended in future to handle other colour spaces such as linear "Adobe RGB".

@keithpaton
Copy link
Author

That sounds good too. I'm sure there are a few cases, particularly for print use where it's important that the source Colour Space is preserved in the output. I'm conscious that this is a very specific requirement that most people using sharp may not need.

@lovell lovell changed the title CMYK TIFF converted to RGB TIFF Add toColourspace method (was: CMYK TIFF converted to RGB TIFF) Jul 11, 2015
@lovell
Copy link
Owner

lovell commented Jul 11, 2015

Note to implementer/self:

The best API addition here is probably toColourspace(colourspace), where colourspace is one of sharp.colourspace.srgb (default) or sharp.colourspace.cmyk.

This should change the output colourspace and, when withMetadata is used, inject the relevant ICC profile.

For multi-English consistency, colourspace should be aliased as colorspace too.

@lovell
Copy link
Owner

lovell commented Jul 2, 2016

Note to implementer/self:

Ensure any new ICC profiles added as part of this (e.g. P3) are suitable for packaging with Debian - see #486

@lovell
Copy link
Owner

lovell commented Aug 18, 2016

v0.16.0 adds the toColourspace operation that provides some of what's needed here. The next step would be to accept ICC profiles (via withMetadata?) to attach to the output image.

@lovell
Copy link
Owner

lovell commented Sep 3, 2016

As promised, my thoughts on how the withMetadata API could support ICC profiles and colourspaces for a number of scenarios:

1:

sharp(rgbInput)
  .withMetadata()
  ...

should inject the default sRGB profile.

2:

sharp(rgbInput)
  .toColorspace('cmyk')
  .withMetadata()
  ...

should inject the default CMYK profile.

3:

sharp(rgbInput)
  .withMetadata({
    profile: '/path/to/alternative-rgb-profile.icc'
  })
  ...

should inject the specified RGB profile (no colourspace conversion).

4:

sharp(rgbInput)
  .toColorspace('cmyk')
  .withMetadata({
    profile: '/path/to/alternative-cmyk-profile.icc'
  })
  ...

should inject the specified CMYK profile.

5: The existing behaviour of removing any profile when withMetadata is not used should remain.

@mhirsch
Copy link
Contributor

mhirsch commented Sep 6, 2016

This sounds very doable. How much error detection / sanity checing should sharp be responsible for? For example, what if we have an sRGB input image, and the icc profile is specified as in case 3 above, but the icc profile is for a cmyk image? Should withMetadata() ever perform a color conversion? Should it throw an error if the colorspace doesn't match the profile?

@lovell
Copy link
Owner

lovell commented Sep 6, 2016

@mhirsch Thank you. Sadly I suspect there's very little we can do given all this API will be doing is attaching otherwise random bytes to the output image's metadata.

I've seen all manner of invalid colour space and profile combinations and will remain happy as long as sharp remains robust in the face of such input data.

@mhirsch
Copy link
Contributor

mhirsch commented Oct 2, 2016

@lovell I'm taking a look at this again after a long break. What motivated me to open #550 was the exact error noted in #584. My intention with withIcc() was to be able to provide the ICC profile that should be used in the output colorspace conversion. However, in looking at implementing this now with the API you laid out above, you specify that "no colorspace conversion" is to be done with the profile argument to withMetadata(). It sounds as if, first the image is converted to CMYK somehow, and then the profile is appended as a binary blob without much regard to its content. I don't see a workable way to do this with vips. I think it would require the use of another library.

What if we were to modify the proposed withMetadata() API to include a specific inputProfile and outputProfile option, to be used in any requested colorspace conversion that sharp may need to handle? If inputProfile is left out, we first check for embedded profiles, then use the default sRGB profile. We can further let sharp use the default sRGB and CMYK profiles to perform sRGB/CMYK colorspace conversions when no profiles are specified, and we would otherwise get the "no route" error from vips. And we can retain the behavior that any call to withMetadata() causes sharp to embed the output profile in the output image.

Does this sound acceptable?

@lovell
Copy link
Owner

lovell commented Oct 3, 2016

@mhirsch How about exposing control over the input profile through the existing input options, something like:

sharp(input, { profile: '/path/to/profile.icc' })...

We should be able to use libvips' icc_transform operation for this. When a custom profile is specified, set embedded to FALSE and input_profile to the path.

@lovell lovell added this to the v0.17.0 milestone Oct 13, 2016
@rn4391
Copy link

rn4391 commented Nov 1, 2016

Is the above change in the withMetadata API being taken up? Also, would it be feasible to change the API to something like this

sharp(rgbInput) .withMetadata({ profile: true })

which uses the embedded profile.

sharp(rgbInput) .withMetadata({ profile: 'path/to/profile.icc' })

Specifying a string path would use that particular profile in output instead of the embedded profile

@lovell lovell removed this from the v0.17.0 milestone Nov 4, 2016
@samkelleher
Copy link

Just to chime in with a use case for Display P3 as links with relevant conversation above.

The iPhone 7 now by default generates JPEGs with the Display P3 ICC profile attached (previously generations didn't attach any profiles). They do this because these new phones and the latest MacBooks have P3 monitors. WebKit / Safari (desktop + mobile) will correctly render P3 tagged images on these P3 supporting devices. Any untagged image will be treated as sRGB.

Sharp will currently embed a default sRGB profile regardless of what profile was already in place if the withMetadata() option is used. So a P3 image that is say resized using Sharp, will become tagged sRGB and thus not display the intended colors when loaded on these P3 devices.

Would love the option to specify the profile using withMetadata() or withIcc() or somesort, or to use existing one if present and it's not sRGB.

I'm still investigating if the output images are still P3 images just with the wrong profile or if they have actually had their colors 'downgraded' to sRGB.

In a perfect world it would be great to have the withIcc() option as I still wouldn't want to bloat web images with EXIF stuff, but do want to maintain the ICC embedded profile.

@lovell
Copy link
Owner

lovell commented Nov 21, 2016

"So a P3 image that is say resized using Sharp, will become tagged sRGB and thus not display the intended colors when loaded on these P3 devices."

If there's an embedded P3 profile in the input, sharp/libvips currently uses this to convert pixels to non-linear sRGB value via lcms2. This means output colours will be as close as possible, but conversion from P3 to sRGB will reduce the gamut, especially in the orange region.

@samkelleher
Copy link

Thanks for chiming in @lovell , this is what I sussed out in my tests last night. So if a withIcc() method or some sorts could be introduced, it would be great to use Sharp to be able to manipulate P3 tagged images without automatically downgrading them to sRGB in the process.

Been using the new devices and the difference of a sunset photo in a P3 image vs one that has been downgraded to sRGB is surprisingly dramatic when compared side by side. 🌇 😍

@lovell
Copy link
Owner

lovell commented Dec 7, 2016

@caesar The current conversion to sRGB would no longer be forced as a result of this work, although it would still remain the default behaviour unless told otherwise.

@caesar
Copy link

caesar commented Dec 7, 2016

@lovell that's fantastic news!

It's not clear to me from the above – is this currently being implemented, or still in the API proposals stage? Is it going to be a big job / take a long time to implement?

I would happily offer to help with the implementation, but I am not familiar with libvips and not that knowledgable on image processing in general, so I fear I don't have much to offer...

@CvX
Copy link

CvX commented Mar 16, 2017

How's the progress on non-sRGB processing? Any tasks we could help with, any testing needed to be done?

@rn4391
Copy link

rn4391 commented Apr 17, 2017

Hi

What is the progress on this API implementation?

I was having a problem with the CMYK image. Some clarity would help.

If I have a CMYK image that is initially without a profile and I attach a profile like this one https://github.com/lovell/sharp/blob/master/lib/icc/cmyk.icm to the file. Then I use .toColorspace("cmyk") and still get the error vips_colourspace: no known route between 'cmyk' and 'srgb'. Is this the correct way to go about preserving the colorspace in the transformation with the current API?

@lovell
Copy link
Owner

lovell commented Apr 17, 2017

@RNanwani If there's an embedded profile on the input image, sharp will currently always attempt to use it to convert to the device-independent sRGB colour space.

Whilst there is a toColorspace operation, it can be improved (as you've seen). The additions under discussion here will allow non-sRGB profiles for output images, which I think is what you seek.

@shot131
Copy link

shot131 commented May 17, 2017

Then I convert jpg image from srgb to cmyk, get an error:

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: vips_colourspace: no known route between 'srgb' and 'cmyk'

Is this corrent code, or I need somthing else?

sharp('path/to/file_input.jpg')
	.toColorspace('cmyk')
	.withMetadata({
	  	profile: 'path/to/profile.icc'
	})
	.toFile('path/to/file_output.jpg');

@lovell
Copy link
Owner

lovell commented May 17, 2017

@shot131 This is, so far, a discussion about what future possible modifications to the withMetadata API are required.

@caesar
Copy link

caesar commented Jun 14, 2017

It would be really great to get this issue solved so that Sharp can be used with CMYK (and other non-sRGB) images.

Is there anything we can do to help?

@jkk
Copy link

jkk commented Sep 23, 2017

Chiming in with my use case: ideally I would like to retain the embedded profile of the input image (no conversion), and strip all other metadata. I'm generating thumbnails for photos that photographers are uploading, and the color should be accurate, but none of the other metadata matters. I'm using imagemagick currently and this seems to be the only feature that sharp can't match.

@lovell lovell changed the title Add toColourspace method (was: CMYK TIFF converted to RGB TIFF) Expose setting ICC profile via withMetadata Feb 5, 2018
@BiancoA
Copy link
Contributor

BiancoA commented Mar 16, 2018

Coming with my use case: I have some images (not sRGB) without ICC profile, I have a ICC profile that I would like to use to convert them to sRGB,

As far as I got this is not yet possible with sharp, right?

@lovell
Copy link
Owner

lovell commented Mar 16, 2018

Thanks everyone for all the comments and suggestions on this issue; please keep them coming.

It's clear there are many use cases required around colour profiles that sharp does not currently support so I will split this issue into a few different tasks e.g. custom input profiles, custom output profiles, custom profiles for colourspace conversion.

@teohhanhui
Copy link
Contributor

Will this require any changes in libvips?

@kevinswarner
Copy link

So, I am confused. Is it possible to start with a source file that is CMYK, perform operations on it, and then save it back out as a CMYK?

source1.tif (CMYK)
do some work on it...
output.tif (CMYK)

When I attempt to use toColorspace('cmyk'), I get the error "vips_colourspace: no known route from 'srgb' to 'cmyk'".

It appears this is not possible, but I need to know for sure so I can decide how / if to use this library. I appreciate any guidance on this. Thanks!

@caesar
Copy link

caesar commented May 19, 2018 via email

@kevinswarner
Copy link

Thanks Caeser. I guess one approach is to do the work with Sharp, but then use a different approach to convert as a post-processing step. Do you know if the issue is with Sharp or with libvips?

@teohhanhui
Copy link
Contributor

@adityapatadia
Copy link

@lovell can we split this issue to track them better? If needed I would love to offer my axe to resolve the issue especially around the output CMYK part.

@lovell
Copy link
Owner

lovell commented Aug 5, 2018

I've split this discussion into 3 new issues. Please feel free to +1, subscribe to and comment on as many as you're interested in.

Thank you all for the comments and suggestions so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests