Page 1 of 2

Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T01:54:01-07:00
by tricon
Hi everybody,

I've been working with Imagemagick for a while now (using it in a web project, thus using PHP to work with it), and I've ran into a problem. I hope a smart mind here can help me.

I have to implement a color picker in my project (again, web based), which is no hassle at all. Done that with some fancy vanilla css and javascript. But I also have to display the CMYK values of a color, which the user selects. And that is where my problem starts. All the rough calculations on the web convert from RGB to CMYK without respecting any ICC profile at all. So they come up with e.g. rgb(0,255,255) = cmyk(100,0,0,0). I need this conversion to be more accurate with respect to the gamut of a specific ICC profile (CoatedFogra39 to be precise). I have the profile, since it is openly available. But I do not know how to calculate single color values with respect to this profile (and then back to RGB with respect to AdobeRGB1998).

All that is necessary, in order to give the user visual feedback, like in Photoshop, when a selected RGB color is out of gamut of the active ICC profile, and give him the nearest suggested color which lies inside the given gamut. In the attached image you can see that 1) Photoshop converts rgb(0,255,255) to cmyk(84,0,21,0), which lies inside the gamut range of CoatedFogra39, and 2) suggests to change the color to the nearest rgb value inside the cmyk gamut, which corresponds to cmyk(84,0,21,0), thus, if one clicks on the suggested color, the rgb changes to rgb(86,169,198).

Is there a way I can achieve this sort of single color value calculation with Imagemagick?
I need to retrieve values as a string, array or whatever format, so I can return it to the script which displays the numbers in the color picker.

Thanks a huge-ton for reading my wall of text.

Tricon

Image

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T08:19:09-07:00
by snibgo
It sounds as if you want an image with a single pixel to undergo a round-trip from sRGB to CMYK and back, such as:

Code: Select all

convert xc:rgb(0,255,255) -profile sRGB.icc -write txt: -profile CoatedFOGRA39.icc -write txt: -profile sRGB.icc txt:
The output is:

Code: Select all

# ImageMagick pixel enumeration: 1,1,65535,srgb
0,0: (0%,100%,100%)  #0000FFFFFFFF  cyan
# ImageMagick pixel enumeration: 1,1,65535,cmyk
0,0: (99.8795%,0%,21.9852%,0%)  #FFB0000038480000  cmyk(99.8795%,0%,21.9852%,0%)
# ImageMagick pixel enumeration: 1,1,65535,srgb
0,0: (11.3725%,75.0423%,77.4777%)  #1D1DC01BC657  srgb(11.3725%,75.0423%,77.4777%)
The first "-write txt:" and output isn't needed. It just proves we get out what we put in, and shows we should ignore the last item on the output lines.

We are listing the pixels in the image. "0,0" is the coordinate of the only pixel.

Then we get the percentages, of the channels given at the end of the previous "#" line.

Then, if we prefer, we get the hex number.

If you try in-gamut numbers, eg rgb(100,110,120), the round-trip gives a final output number very close to the input.

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T14:13:06-07:00
by tricon
Hello snibgo,

thanks a lot for your reply.
I thought of a similar process already, but the one thing that keeps me back is the workload necessary for this procedure.
Currently I am extracting the color values in this way:

1) create the 1x1 image with the respective color profiles

Code: Select all

exec("convert xc:'rgb(0,255,255)' -profile AdobeRGB1998.icc -profile CoatedFOGRA39.icc color_cmyk.jpg");
2) extract the cmyk color values to be used later

Code: Select all

$cmyk = exec("convert color_cmyk.jpg -format '%[fx:int(100*c)], %[fx:int(100*m)], %[fx:int(100*y)], %[fx:int(100*k)]' info:- ");
result: 80, 24, 0, 0

3) convert image back to rgb, again with the correct color profile

Code: Select all

exec("convert color_cmyk.jpg -profile AdobeRGB1998.icc color_rgb.jpg");
4) extract the new rgb color values to be used later

Code: Select all

$rgb = exec("convert color_rgb.jpg -format '%[fx:(255*r)], %[fx:(255*g)], %[fx:(255*b)]' info:- ");
result 80, 172, 195

One thing I've noticed is that the first file (color_cmyk.jpg) is 639kb in size, while the second image (color_rgb.jpg) is only 864b. The CoatedFOGRA39 profile file is 545kb and the AdobeRGB1998 file is just 1kb. So it seems the profiles (at least the cmyk one) are embedded somewhat uncompressed. That might also be a reason, why the calculation takes so "long". In my tests the waiting time for the server reply is about 4-5sec. This is a bit problematic if I want to update the color picker in near-realtime.

Do you maybe have any suggestion for me, or an idea in which direction I could do more research?

Thanks a lot again
Tricon

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T14:29:14-07:00
by snibgo
Do you need those intermediate files? It would be far quicker without. You can "-write info:" then carry on with the rest of the command. I don't know how to read the output with PHP.

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T16:19:59-07:00
by tricon
I would not need the files, no.
I've tried what you suggested and used this:

Code: Select all

$img_cmyk = exec("convert xc:'rgb(0,255,255)' -profile AdobeRGB1998.icc -profile CoatedFOGRA39.icc info:- ");
Echoing the variable gives me this:

Code: Select all

xc:rgb(0,255,255) XC 1x1 1x1+0+0 16-bit ColorSeparation DirectClass 7.850u 0:02.079 
Could you tell me, how I can continue to work with this string? The command I use next, to extract the pixel colors, does not work, if i store the info of the cmyk jpg in a variable this way.

Code: Select all

$col_cmyk = exec("convert " . $img_cmyk . " -format '%[fx:int(100*c)], %[fx:int(100*m)], %[fx:int(100*y)], %[fx:int(100*k)]' info:- ");
This (2nd step) gives no output what so ever.

Any idea?

Thank you
Tricon

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T16:25:27-07:00
by fmw42
try sending std error to stdout

Code: Select all

$col_cmyk = exec("convert " . $img_cmyk . " -format '%[fx:int(100*c)], %[fx:int(100*m)], %[fx:int(100*y)], %[fx:int(100*k)]' info:- 2>&1");

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-20T16:28:03-07:00
by snibgo
Sorry, I don't know PHP.

If you use a single "convert" command, you won't need intermediate files. But if you keep it as separate "convert" commands, you do need intermediate files.

You set $img_cmyk to be useless text output. You can't then give that to "convert".

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-21T04:06:34-07:00
by tricon
Hello again and thanks for the suggestions.

I've gone your way, snibgo, and used a single convert command without saving intermediate files.
This is what I'm currently doing:

Code: Select all

exec("convert xc:'rgb(0,255,255)' -profile AdobeRGB1998.icc -profile CoatedFOGRA39.icc -write txt:cmyk.txt -profile AdobeRGB1998.icc txt:rgb.txt");
cmyk.txt:

Code: Select all

# ImageMagick pixel enumeration: 1,1,65535,cmyk
0,0: (52631,    0,15802,    0)  #CD9700003DBA0000  cmyk(80.3098%,0%,24.1123%,0%)
rgb.txt

Code: Select all

# ImageMagick pixel enumeration: 1,1,65535,rgb
0,0: (20608,44283,49824)  #5080ACFBC2A0  rgb(31.4458%,67.5715%,76.0266%)
Extracting the cmyk and rgb values from that would not be a problem, but this method still takes roughly 4-5sec to produce the text files.
If I do not apply the color profile calculations, everything is a lot faster, evidently. Do you believe there could be a faster method to get these values?
I have not tried it yet, but do you think parsing a nosql db with all rgb color entries as strings to get the corresponsing cmyk value, also as a string, would yield faster results (near real-time)?

Thank you
Tricon

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-21T04:25:47-07:00
by snibgo
Perhaps some other tool is available for converting single colours, and it might be faster than IM for this.

If you only want 8-bit resolution, a look-up table would be much quicker. The table wouldn't be huge: 256*256*256 = 17 m entries.

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-25T16:01:11-07:00
by tricon
Hi again - sorry to rewake this post, but I'd have another question, if anyone could assist me.
Is it possible to use the -write command in conjunction with the -format command, to store only the cmyk color values in a txt file, and then repeatedly adding new values to the same file?

If at all possible, I imagined the first txt file to consist, for example, only of:

Code: Select all

80.3098%,0%,24.1123%,0%
and then adding to this text file another value, so it looks, e.g., like this:

Code: Select all

80.3098%,0%,24.1123%,0%
61.2169%,0%,28.9175%,0%
Is this possible with the imagemagick cli tools?

Thanks again
Tricon

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-25T16:31:57-07:00
by fmw42
IM in combination with your OS. For unix, just redirect the result to the file using

convert ..... txt >> textfile.txt

If using PHP, then the exec command can simple get the data output from txt without the redirect and you can then write it to your textfile.

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-25T16:56:25-07:00
by snibgo
As Fred says, your shell can append new lines to existing text files.

You can get the format you want directly, eg:

Code: Select all

convert ^
  xc:khaki -colorspace CMYK ^
  -precision 9 ^
  -format "%%[fx:100*p{0,0}.c]\%%,%%[fx:100*p{0,0}.m]\%%,%%[fx:100*p{0,0}.y]\%%,%%[fx:100*p{0,0}.k]\%%" ^
  info:
This is Windows BAT syntax. Other shells need variations, eg turn "%%" and "\%%" to "%", etc.

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-25T17:16:47-07:00
by fmw42
This might be simpler (in unix syntax) as long as the colorspace is not rgb/srgb which would return khaki. Otherwise, snibgo's command will need to be modified for colorspaces with different numbers of channels. But if you just want cmyk, then either is fine. My way works automatically with other colorspaces such as HSL, etc.

Code: Select all

convert xc:khaki -precision 4 -colorspace cmyk -format "%[pixel:p{0,0}]" info:
cmyk(0%,4.167%,41.67%,5.882%)

see
http://www.imagemagick.org/script/fx.php
http://www.imagemagick.org/Usage/transform/#fx_escapes

To put it into a file

Code: Select all

convert xc:khaki -precision 4 -colorspace cmyk -format "%[pixel:p{0,0}]" info: >> txtfile.txt
The >> means to add to the file, so that each time you run the command (e.g with different colors), it will add the result to your txtfile. If you want to overwrite, then use just one >.

This should work in PHP exec, but you can access the text results from exec without the >> and write them via PHP to a text file if you want.


P.S. snibgo's command can be simplified as just

Code: Select all

convert ^
  xc:khaki -colorspace CMYK ^
  -precision 9 ^
  -format "%%[fx:100*c]\%%,%%[fx:100*m]\%%,%%[fx:100*y]\%%,%%[fx:100*k]\%%" ^
  info:

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-25T23:27:27-07:00
by tricon
You guys are amazing, thanks a ton.
I'll have to give this a try immediately when I get the chance ;)

Re: Convert color values from RGB to CMYK with ICC profile

Posted: 2014-05-26T00:27:15-07:00
by tricon
Could there be a bug with the variables used inside an -fx function?
If I use this format notation:

Code: Select all

-format '%[fx:int(100*c)], %[fx:int(100*m)], %[fx:int(100*y)], %[fx:int(100*k)]'
the results are for example this:
RGB(0,255,255) -> "80, 24, 0, 0"
RGB(255,0,0) -> "0, 82, 90, 0"
RGB(255,0,255) -> "27, 0, 75, 0"
RGB(0,255,0) -> "89, 100, 0, 0"

All of these cmyk values are absolutely fine, except that the M and Y values seem to be swapped (compared to Photoshop). The numbers are fine, it's just the places of those two colors which switched positions apparently. Is this a bug or am I missing something? Any Idea?

Thanks
Tricon