Skip to content

Reading CMYK JPEG images with Java ImageIO

by werner on August 15th, 2011

As mentioned in an earlier article, I have written a reader for JPEG images with CMYK color data in Java.

This reader can be used with Java ImageIO. I have made a demo including source code: CMYKDemo.jar. You can download this file and then double click it to run the demo. The demo is a window onto which you can drop a CMYK image. You can unzip the file to get the source code.

Here is how to use the CMYKJPEGImageReader with ImageIO:

  1. Register the reader with ImageIO.This can be done by putting the file CMYKDemo.jar into the class path.Alternatively – if you want to build the reader from source and integrate it into your own .jar file – you can create a file named META-INF/services/javax.imageio.spi.ImageReaderSpi and put it in the class path.

    The file should contain the following line:

    ch.randelshofer.media.jpeg.CMYKJPEGImageReaderSpi
  2. Read an image with ImageIO.Unfortunately, the ImageIO.read method is not a reliable way for reading a JPEG CMYK image.
    ImageIO.read(file);

    This does not work, because ImageIO already provides a JPEG image reader. There is no way to specify in which order the readers shall be used. So, sometimes, ImageIO will use our CMYKJPEGImageReader and succeed, and sometimes it will try to use its own reader, and fail.

    Therefore, we need to use the following code snippet, which tries out all JPEG readers until it succeeds.

    Image img = null;
    ImageInputStream iis = new FileImageInputStream(file);
    try {
        for (Iterator<ImageReader> i = ImageIO.getImageReaders(iis); 
             img == null && i.hasNext(); ) {
            ImageReader r = i.next();
            try {
                r.setInput(iis);
                img = r.read(0);
            } catch (IOException e) {}
        }
    } finally {
        iis.close();
    }
    return img;

As always, my code examples are available for free under the Creative Commons Attribution license. Enjoy.

 

Update 2014-01-22:
I have closed the comment section for this article, because the CMYKJPEGImageReader class is obsolete. Take a look at the Monte Media Library for newer versions of this class.

From → Image & Video, Java

20 Comments
  1. Thank you! we needed to fix this bug to read multiple images in Photoshop.

  2. Stefan Baur permalink

    Hey there

    I am very glad having found this website! :)

    I think I’m struggling with the case JFIF SOI magic that seems unsupported.

    That’s why I wanted to ask if you have worked on this code and made updates since August?

    One of the problematic images is: http://baurs.ch/unsupported.jpg

    Great stuff here, thank you!
    Grüessli, Stefan

  3. Hi Stefan,

    The image that you linked is loaded fine by the code that I wrote. The color profile is not interpreted properly, but this is caused by issues in the color profile classes in J2SE.

    To fix this, you would have to find (or write) a color profile class, which can handle this kind of color profile, and plug it into the reader.

    hth,
    Werner

  4. Thank you for making this CMYK reader. I’m going to add it to the next version of jAlbum now. Seems to work great. The only glitch I found was that it’s slow at quickly grabbing metadata only from the images, but I could easily simply ignore your reader for that task and use Sun’s reader for metadata grabbing.

  5. Hi David,

    I am glad my CMYK reader is of use to you.
    I have not implemented meta-data reading in this class. So, ImageIO may have to jump through some hoops to get it.

    I use my own specialized classes for EXIF meta-data and thumbnail reading which bypass ImageIO altogether. Some of it is in MultiShow (available from my web-site). For a fast thumbnail-reader you may want to take a look at this nice article by Jeremy Woods. My implementation is very similar.

  6. Thanks for the link.

  7. Hi. One user got multiple out of memory errors when using your reader. Here is the stack trace (sorry, I don’t have the image that causes this):

    java.lang.OutOfMemoryError: Java heap space at sun.awt.image.ByteInterleavedRaster.getSamples(ByteInterleavedRaster.java:899) at ch.randelshofer.media.jpeg.CMYKJPEGImageReader.convertInvertedYCCKToCMYK(CMYKJPEGImageReader.java:603) at ch.randelshofer.media.jpeg.CMYKJPEGImageReader.readRGBImageFromInvertedYCCK(CMYKJPEGImageReader.java:363) at ch.randelshofer.media.jpeg.CMYKJPEGImageReader.read(CMYKJPEGImageReader.java:240) at ch.randelshofer.media.jpeg.CMYKJPEGImageReader.readHeader(CMYKJPEGImageReader.java:117) at ch.randelshofer.media.jpeg.CMYKJPEGImageReader.getImageTypes(CMYKJPEGImageReader.java:70) at se.datadosen.util.FileFilters.loadImage(FileFilters.java:509) at se.datadosen.jalbum.AlbumBean$StandardImageProcessor.processImage(AlbumBean.java:4258) at se.datadosen.jalbum.AlbumBean$StandardImageProcessor.processImages(AlbumBean.java:4190) at se.datadosen.jalbum.AlbumBean$I

  8. Hi David,

    I suggest increasing the heap size of the JVM.

  9. Carl Buxbaum permalink

    Thanks you so much! Sun/Oracle should pay you for your effort. can’t believe this but has been around for 8 years. I just discovered that JAI was not working on 64 Bit java, and was concerned I would not find an alternative.

  10. Santhosh Reddy permalink

    Thanks for implementing this program. It really helped us

  11. Erçin permalink

    Hi firstly thanks for this good post. I am trying to convert CMYK images to RGB images but with this code i always getting “Unsupported Image Type” exception it’s only me? Can you help me about this you can send e-mail too.
    Thank You.

  12. Hi Erçin,

    Maybe your image type is indeed not supported.
    You may want to try if MultiShow can read your image.
    If it can, then it is ‘only you’. ;-)
    MultiShow can be downloaded from my homepage.

  13. The ImageReaderSpi file should contain the correct package:

    org.monte.media.jpeg.CMYKJPEGImageReaderSpi

  14. Marc Gorzala permalink

    Hello,

    I don`t know whether it is intended or by accident:

    the source for org.monte.media.io.ImageInputStreamImpl2 is missing.

    So the question is: intended or by accident?

    Thank you for your work,

    Marc

  15. David permalink

    I know this isn’t the right place for this so delete after reading.

    I enjoy your program but I can’t find the help files.
    If they exit I would suggest putting them in a more obvious place.
    If they don’t, I’m sure I’m missing some handy dandy features.

    Again, great program.

    Dave

  16. Guest permalink

    Linux Image object is null.
    Window and Linux, the behavior difference between

  17. Andrey permalink

    Hello. Thank you for making this CMYK reader. Thanks to your programs plus I understand how the JPG-file. I learned how to read it to segment how to handle markers, how to read it ICC-profile and use it to convert the color space.

  18. chris permalink

    hello
    just remarked that the source of ImageInputStreamImpl2.class is not in the jar; and the question has been asked before – what is the status here?
    thx
    kind regards
    chris

  19. Berno permalink

    Many thanks for sharing this code!

    If I put the above code in a loop, reading an CMYK JPEG multiple times, I get an OutOfMemoryError after some iterations. I use Java 7.

    To fix this I need to call the dispose() method after using the JPEGImageReader.

    So the example looks like:

    Image img = null;
    ImageInputStream iis = new FileImageInputStream(file);
    [...]
    try {
    ….r.setInput(iis);
    ….img = r.read(0);
    } catch (IOException e) {
    } finally {
    ….if (r != null) {
    ……..r.dispose(); // <– here
    ….}
    }

    In addition each usage of the JPEGImageReader in CMYKJPEGImageReader has to be adapted accordingly, e.g.:

    public static BufferedImage readRGBImageFromCMYK(…) throws IOException {
    ….ImageInputStream inputStream = null;
    ….ImageReader reader = createNativeJPEGReader();
    ….BufferedImage image = null;
    ….try {
    ……..inputStream = (in instanceof ImageInputStream) ? (ImageInputStream) in : ImageIO.createImageInputStream(in);
    ……..reader.setInput(inputStream);
    ……..Raster raster = reader.readRaster(0, null);
    ……..image = createRGBImageFromCMYK(raster, cmykProfile);
    ….} finally {
    ……..if (reader != null) {
    …………reader.dispose(); // <– here
    ……..}
    ….}
    ….return image;
    }

    Can anyone confirm this or do I something completely wrong?

  20. @Berno,

    You are supposed to explicitly call dispose() on an ImageReader object. Please take a look at the API documentation of javax.imageio.ImageReader.

Comments are closed.