15

first I'd like to explain the situation/requirements that lead to the question:

In our web application we can't support CMYK images (JPEG) since IE 8 and below can't display them. Thus we need to detect when someone wants to upload such an image and deny it.

Unfortunately, Java's ImageIO won't read those images or would not enable me to get the detected color space. From debugging it seems like JPEGImageReader internally gets the color space code 11 (which would mean JCS_YCCK) but I can't safely access that information.

When querying the reader for the image types I get nothing for CMYK, so I might assume no image types = unsupported image.

I converted the source CMYK image to RGB using an imaging tool in order to test whether it would then be readable (I tried to simulate the admin's steps when getting the message "No CMYK supported"). However, JPEGImageReader would not read that image, since it assumes (comment in the source!)3-component RGB color space but the image header reports 4 components (maybe RGBA or ARGB) and thus an IllegalArgumentException is thrown.

Thus, ImageIO is not an option since I can't reliably get the color space of an image and I can't tell the admin why an otherwise fine image (it can be displayed by the browser) would not be accepted due to some internal error.

This led me to try JAI ImageIO whose CLibJPEGImageReader does an excellent job and correctly reads all my test images.

However, since we're deploying our application in a JBoss that might host other applications as well, we'd like to keep them as isolated as possible. AFAIK, I'd need to install JAI ImageIO to the JRE or otherwise make the native libs available in order to use them, and thus other applications might get access to them as well, which might cause side effects (at least we'd have to test a lot to ensure that's not the case).

That's the explanation for the question, and here it comes again: Is there any pure Java alternative to JAI ImageIO which reliably detects and possibly converts CMYK images?

Thanks in advance,

Thomas

Thomas
  • 87,414
  • 12
  • 119
  • 157

4 Answers4

14

I found a solution that is ok for our needs: Apache Commons Sanselan. This library reads JPEG headers quite fast and accurate (at least all my test images) as well as a number of other image formats.

The downside is that it won't read JPEG image data, but I can do that with the basic JRE tools.

Reading JPEG images for conversion is quite easy (the ones that ImageIO refuses to read, too):

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new FileInputStream( new File(pFilename) ) );
BufferedImage sourceImg = decoder.decodeAsBufferedImage();

Then if Sanselan tells me the image is actually CMYK, I get the source image's raster and convert myself:

for( /*each pixel in the raster, which is represented as int[4]*/ )
{  
   double k = pixel[3] / 255.0;

   double r = (255.0 - pixel[0])*k;
   double g = (255.0 - pixel[1])*k;
   double b = (255.0 - pixel[2])*k;
}

This give quite good results in the RGB images not being too bright or dark. However, I'm not sure why multiplying with k prevents the brightening. The JPEG is actually decoded in native code and the CMYK->RGB conversion I got states something different, I just tried the multiply to see the visual result.

If anybody could shed some light on this, I'd be grateful.

Tilman Hausherr
  • 17,731
  • 7
  • 58
  • 97
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • I hit the same problem with JAI and CMYK JPEG images. Do you use that approach already in production? How is your experience? I guess you use `Sanselan.getImageInfo(...).getColorType() == ImageInfo.COLOR_TYPE_CMYK` ([ImageInfo](http://commons.apache.org/sanselan/api-release/org/apache/sanselan/ImageInfo.html)) to check if it's a CMYK image, right? – Benedikt Waldvogel Nov 16 '11 at 23:39
  • @bene Yes, we're first checking if Sanselan found an ICC profile and if so take its color space type but if that fails we use `ImageInfo.getColorType()`. We're using this in production and didn't have any problems so far, however, I must admit that we're currently not handling many images. In future we will have our own media database which will handle thousands of images and then we'd have more reliable information on the production readyness of that approach. Just one side note: converting CMYK Tiffs produces some noticable tinting and we didn't manage to get rid of that yet. – Thomas Nov 17 '11 at 09:04
  • 1
    Today I hit an image where Sanselan’s ICCProfile has ColorSpaceType CMYK while ImageInfo’s ColorType is RGB. The color space is actually RGB. Wondering if you had the same problem. – Benedikt Waldvogel Feb 07 '12 at 15:27
  • @bene We didn't have such a problem so far, but I could have a look at our sources and the sanselan ones. Maybe someone else did have that problem without me knowing it and adapted our code. I'd also be interested in that image, could you post a link to it? – Thomas Feb 07 '12 at 17:41
  • For example this one: http://images.internetstores.de/products/20900750_Paloma_frei%5B1024x768%5D.jpg – Benedikt Waldvogel Feb 07 '12 at 20:26
5

I've posted a pure Java solution for reading all sorts of JPEG images and converting them to RGB.

It's built on the following facts:

  • While ImageIO cannot read JPEG images with CMYK as a buffered image, it can read the raw pixel data (raster).
  • Sanselan (or Apache Commons Imaging as it's called now) can be used to read the details of CMYK images.
  • There are images with inverted CMYK values (an old Photoshop bug).
  • There are images with YCCK instead of CMYK (can easily be converted).
Community
  • 1
  • 1
Codo
  • 75,595
  • 17
  • 168
  • 206
3

Beware of another post as the Java 7 does not allow to use directly Sun's implementation without special parameters as indicated in import com.sun.image.codec.jpeg.*.

Community
  • 1
  • 1
tsmets
  • 91
  • 4
1

In our web application we can't support CMYK images (JPEG) since IE 8 and below can't display them. Thus we need to detect when someone wants to upload such an image and deny it.

I don't agree with your "Thus we need to detect when someone wants to upload such an image and deny it". A much more user-friendly policy would be to convert it to something else than CMYK.

The rest of your post is a bit confusing in that regards seen that you ask both for detection and conversion, which are two different things. Once again, I think converting the image is much more user-friendly.

No need to write in bold btw:

Is there any pure Java alternative to JAI ImageIO which reliably detects and possibly converts CMYK images?

Pure Java I don't know, but ImageMagick works fine to convert CMYK image to RGB ones. Calling ImageMagick on the server-side from Java really isn't complicated. I used to do it manually by calling an external process but nowadays there are wrappers like JMagick and im4java.

SyntaxT3rr0r
  • 27,745
  • 21
  • 87
  • 120
  • As for user friendlynes: converting CMYK to RGB sometimes brightens the colors a bit and thus we'd like the user to convert it manually and confirm the colors are still ok. In another application we are free to convert regardless of this, thus I asked for both. – Thomas Jul 26 '11 at 13:05
  • `No need to write in bold btw:` - that was just to mark the question for those who don't want to read the clutter in between :) – Thomas Jul 26 '11 at 13:05
  • Relating `ImageMagick`: that might be an alternative although we'd need to install it as well. Thanks for that suggestion. – Thomas Jul 26 '11 at 13:07