Skip to content

CMYK Images And Browsers And ImageMagick

Dealing with user-uploaded images in web applications has become dead simple - yet not simple enough. JPEG knows profiles and color spaces not every renderer understands correctly. This post illustrates how CMYK JPGs present accross browsers and how one can easily avoid those problems. (And show a neat trick to speed up ImageMagick on multi-core systems.)

Source Of All Evil: Colorspaces

CMYK Image
CMYK Image
CMYK converted to RGB by Adobe Photoshop
CMYK converted to RGB by Adobe Photoshop

CMYK Across Browsers

CMYK Image in Firefox
CMYK Image in Firefox
CMYK Image in Chrome
CMYK Image in Chrome
CMYK Image in Safari
CMYK Image in Safari
CMYK Image in Internet Explorer 8
CMYK Image in Internet Explorer 8

As you can see, Firefox and Chrome render the CMYK Image close enough to what Adobe Photoshop thought the RGB version should look like. Safari on the other hand is losing color. Internet Explorer 8 is, well, showing a broken image - IE8 cannot display CMYK JPGs.

Looks like we might want to convert CMYK to RGB, just in case…

Convert CMYK to RGB

CMYK converted to RGB by ImageMagick
CMYK converted to RGB by ImageMagick
CMYK converted to RGB by Adobe Photoshop
CMYK converted to RGB by Adobe Photoshop

Both RGB versions look fairly similar. Photshop used the sRGB profile, ImageMagick was told to use the AdobeRGB1998 profile. Close enough. I'm a developer, not a designer… ☺

ImageMagick

Whenever I have to deal with image processing, I call up my old buddy IMagick. IMagick is a PECL (PHP extension) wrapping ImageMagick. It offers a much nicer API than wrangling those exec('convert -resize …'); calls by hand. I haven't used GDLib or command line ImageMagick in years - and I'm missing neither. Of course I haven't dealt with shared hosting environments in years either.

Adobe RGB Profiles

To be able to convert the color profiles of your JPEGs, you need an ICC Color Profile, obtainable from Adobe (download links at the very bottom). Download and extract the AdobeRGB1998.icc file from the archive.

You could ignore the profile, but you'd not be too happy about the colors of the converted images. (Feel free to experiment with other ICCs, though!)

Getting Down To Code

The following few lines convert the input image to the RGB colorspace and apply the AdobeRGB1998 profile (for better colors). Since all browsers (including Internet Explorer 8) display grayscale images just fine, we don't need to blow up their file size by converting them to RGB.

$im = new IMagick(__DIR__ .'/input.jpg');
if ($im->getImageColorspace() != IMagick::COLORSPACE_RGB
  && $im->getImageColorspace() != IMagick::COLORSPACE_GRAY) {
    $icc_rgb = file_get_contents(__DIR__ . '/AdobeRGB1998.icc');
    $im->profileImage('icc', $icc_rgb);
    $im->setImageColorspace(IMagick::COLORSPACE_RGB);
}
// do some fun stuff like resizing, cropping or whatever
$im->writeImage(__DIR__ . '/output.jpg');

Optimizing For Multi-Core Hardware

ImageMagick has its downsides, though. One of them being a bad threading implementation. Running the above script on a 24 core machine (fun toy to play with, btw.) I got an execution time of 3 seconds (at a load of 0.5). Running the same script on my 4 year old MacBook pro with only 2 cores took 1 second. Tested with time php test.php, if you care.

The problem is ImageMagick's very very bad threading implementation. For the command line one can specify export MAGICK_THREAD_LIMIT=2. IMagick knows IMagick::setResourceLimit() but lacks IMagick::RESOURCETYPE_THREADS.

Lucky us, we can have a look at the ImageMagick source to find out what the value of IMagick::RESOURCETYPE_THREADS would be. (int) 6 according to resource_.h. Putting that into action:

// limit ImageMagick to single thread
Imagick::setResourceLimit(6, 1);

$im = new IMagick(__DIR__ .'/input.jpg');
if ($im->getImageColorspace() != IMagick::COLORSPACE_RGB
  && $im->getImageColorspace() != IMagick::COLORSPACE_GRAY) {
    $icc_rgb = file_get_contents(__DIR__ . '/AdobeRGB1998.icc');
    $im->profileImage('icc', $icc_rgb);
    $im->setImageColorspace(IMagick::COLORSPACE_RGB);
}
// do some fun stuff like resizing, cropping or whatever
$im->writeImage(__DIR__ . '/output.jpg');

And we get an execution time of 0.18 seconds (which is about 17 times faster). Awesome, done for the day. ☺

By the way: this wouldn't have been necessary, if we had compiled ImageMagick --without-threads.

Comments

Display comments as Linear | Threaded

Sara on :

SaraThanks! I will try to "limit ImageMagick to single thread"

Also, imagick::FILTER_CATROM is faster than Imagick::FILTER_LANCZOS and produces nearly the same result.

One question:

Where do I find AdobeRGB1998.icc, and how do I install it?

Regards

The author does not allow comments to this entry