lovell / icc

JavaScript module to parse International Color Consortium (ICC) profiles
Apache License 2.0
57 stars 6 forks source link

Questions #1

Closed homerjam closed 7 years ago

homerjam commented 8 years ago

Hi Lovell

A question/some advice if you don't mind!

I'm using https://github.com/blueimp/JavaScript-Load-Image to resize images client side for upload. After resizing I'm adding the metadata back to the image for upload. This works great for RGB images, however it's not working with CMYK images - the result has an invalid ICC profile.

cmyk-invalid-icc

What I'd like to do is flag to the uploader images which are not RGB - the best way to do this is by examining the ICC profile (EXIF colorspace data is simply not reliable as far as I can tell). Having looked at this lib I think it could be used in the browser however I still face the issue of extracting the ICC profile from the metadata/header/image in the first place.

So my question - is there a way this lib could be made to work with an image rather than a raw profile? I'm thinking there may be some clues here http://www.littlecms.com/1/iccjpeg.c. Or do you know how one might extract the ICC profile from an image in the browser?

Many thanks for your time

James

homerjam commented 8 years ago

Hi @blueimp - just wondering if you had any thought on the above? Thanks

lovell commented 8 years ago

In JPEG images, ICC profiles are stored in one or more APP2 markers.

Looking at the source of JavaScript-Load-Image, my best guess would be to handle this by adding to metaDataParsers.

icc should be able to parse enough from the first APP2 to detect CMYK vs whatever, but you might need to concatenate multiple occurrences of APP2 marker data into a single Buffer first.

I notice there's a 256 KiB limit. This is probably not enough to "contain all EXIF/ICC/IPTC segments" given CMYK ICC profiles are ~540KiB.

blueimp commented 8 years ago

Hi @homerjam and @lovell,

you're correct, extending metaDataParsers is the right way to go about it. This is the same way the exif extension is implemented.

When I set the default 256 KB limit, I did not do enough research on ICC profiles and admit that the size constraint should be increased. There is however already an option maxMetaDataSize, which allows to increase it manually.

Might be that increasing the size is even enough to preserve the ICC profiles with resized images with the original header information added.

homerjam commented 8 years ago

Hi @blueimp @lovell

I've been experimenting with this and have a working demo:

https://homerjam.github.io/upload-test/

Repo here:

https://github.com/homerjam/upload-test

This is working fine for my needs, but there are a couple of issues.

Firstly on this line I'm adding an arbitrary (in my layman's view) value to the offset - can you tell me why this would be working??!

Secondly, when uploading a CMYK image the resulting file won't open in Photoshop. Running identify on the image produces the following result:

identify: Unknown Adobe color transform code 2 `image.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.

I think whats happening is the transform flag in APP14 is leading the decoder to expect a CMYK/YCCK encoded image but the output from the resized canvas is RGB (I think) - maybe this could be resolved by transforming the RGB pixel information into CMYK? Do either of you guys have any ideas on this?

ref: https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html

@blueimp do you think it's worth creating a load-image plugin for this? Any chance you could help on that : ) My main unknown here is how to get the dependencies in for an AMD/global module...

Thanks for your time! James

blueimp commented 8 years ago

Firstly on this line I'm adding an arbitrary (in my layman's view) value to the offset - can you tell me why this would be working??!

The offset value that is passed to your metaDataParser function is the start of the segment that was identified by the marker that your parser function is defined for (in your case 0xffe2). The first 4 bytes are the marker itself and the length bytes (see https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-meta.js#L91). What comes after that is defined in the specification for that segment, so in this case the specification for ICC profiles. I haven't made myself familiar with ICC profiles yet, but if your "arbitrary value" of 18 works, I guess the remaining 14 bytes are likely some other meta data that are not part of the actual ICC profile. Best to look that up in the specification and comment it in the code, else you're gonna wonder what those 18 bytes were for.

I think whats happening is the transform flag in APP14 is leading the decoder to expect a CMYK/YCCK encoded image but the output from the resized canvas is RGB (I think) - maybe this could be resolved by transforming the RGB pixel information into CMYK? Do either of you guys have any ideas on this?

I'm not sure it's possible, as the specification for ImageData.data, which allows to manipulate a canvas pixel data, expects RGBA data arrays.

Then there's also the issue that RGB and CMYK are not directly convertible, as explained here: http://stackoverflow.com/questions/8869248/use-cmyk-on-web-page/8869896#8869896 That StackOverflow page also contains some sample code for approximations to convert between the different color space.

@blueimp do you think it's worth creating a load-image plugin for this? Any chance you could help on that : ) My main unknown here is how to get the dependencies in for an AMD/global module...

Sure, I can give you any information you need to create a plugin. My answers might be delayed though, as I don't spend much time working on my open source projects anymore (family has priority).

Cheers! :relaxed:

homerjam commented 8 years ago

Hi @blueimp

Thanks for such a detailed response!

Ok I'm going to do some more investigation into the make up of an ICC profile - maybe then I'll tidy this up and try making a plugin.

As for CMYK images - my original goal was to prevent the user uploading these which has now been achieved. But perhaps I'll look into doing an approximate conversion and/or altering the transform flag.

Will keep you updated! Thanks again!

lovell commented 8 years ago

http://www.littlecms.com/1/iccjpeg.c explains the 14 byte offset:

 *  Identifying string  ASCII "ICC_PROFILE\0"  (12 bytes)
 *  Marker sequence number  1 for first APP2, 2 for next, etc (1 byte)
 *  Number of markers   Total number of APP2's used (1 byte)
 *      Profile data        (remainder of APP2 data)

The data from an HTML5 canvas element will be in device-independent (non-linear) sRGB rather than device-dependent, linear RGB.

CMYK is always device-dependent so you'll need to use a profile to convert to another colour space with any degree of accuracy. sharp/libvips uses LittleCMS under the bonnet for this purpose. Good luck!