Converting images from RGB to CMYK


All replies

  • Doesn't seem that way, I pulled out this note from the TIFF 6.0 specification:

    Note: there is no single method of converting RGB data to CMYK data and back.
    In a perfect world, something close to cyan = 255-red, magenta = 255-green, and
    yellow = 255-blue should work; but characteristics of printing inks and printing
    presses, economics, and the fact that the meaning of RGB itself depends on other
    parameters combine to spoil this simplicity.

    You can find the specification here if you want to read more about it.

    Saturday, July 5, 2008 10:20 AM
  • Calle,
    It is interesting, however, that we can go the other way. I found this article: I tried it and it works great. I just can't figure out how to reverse it.

    I will check out your link--thank you for responding...


    Saturday, July 5, 2008 1:25 PM
  • I should mention that I changed the code in his article slightly:

    private bool IsCMYK(Image image)


                // based on


                bool ret = false;




                    int cmyk = (image.Flags & (int)ImageFlags.ColorSpaceCmyk);

                    int ycck = (image.Flags & (int)ImageFlags.ColorSpaceYcck);


                    ret = ((cmyk > 0) || (ycck > 0));


                catch (Exception ex)


                    MessageBox.Show(this, ex.ToString(), App.Name, MessageBoxButtons.OK, MessageBoxIcon.Error);



                return ret;


    Saturday, July 5, 2008 1:27 PM
  • Well, it seems to be the Graphics.DrawImage() method that does the magic. This method is only a wrapper for the GdipDrawImageRectI in gdiplus.dll and there's no detailed info on that method to be found anywhere.
    I guess it's the pixel format of the image from which the Graphics object is created that determines in what format the image will be drawn using DrawImage. So, if you would set the pixel format to some other format than in the image being drawn image data will be converted. Unfortunatily, there's no pixel format defined for CMYK as far as I can see so using the same methods to reverse the format would not be possible.
    You simply have to know how the 4 byte CMYK pixel is converted to the 3 byte RGB pixel. If you could examine a CMYK image along with its converted RGB variant then you might be able to come up with a pattern for converting the image data your self.

    If you have a CMYK image (one you have converted using the code from the article) maybe you could send it to me and I could examine it some more.


    Saturday, July 5, 2008 6:15 PM
  • Sure; I just sent my test images. I verified them in Corel Paint x3 (which is what I used to convert it to CMYK initially.) I was thinking about loading up the Paint.NET source too to see how they handle it (if at all.) Regardless, I appreciate you looking at this. I write book publishing software and we have noticed that the publisher (Amazon) does not handle RGB images so well. I also have to convert the images to 300 DPI, so that is why these are a bit bigger than normal.

    Saturday, July 5, 2008 6:43 PM
  • I'm afraid I've reached a dead-end on this. I've been trying different methods and concepts for hours, sweeping the net for all knowledge there is on this subject, still all I can produce is a CMYK image with badly converted colors.

    First I had to find a way to make .net save an image tagged with the CMYK color space (for e.g. PS to recognize it as being a CMYK image). I found that .net framework 3.0 has added BitmapEncoder classes for Jpeg, Gif, Tiff and some other. There was also a new enum 'System.Windows.Media.PixelFormats' that defined a Cmyk32 value. I could create a BitmapSource using this Cmyk32 pixelformat but the only encoder that accepted a cmyk frame was the TiffEncoder (wonder when MS is planning on finishing that one...).
    Well, I accepted the Tiff format and tried to find a way to convert the pixels from an RGB image. I found on countless sites the formula for converting an RGB pixel to its CMYK variant. But this was simply "not good" and did not at all reveal the true converting to a CMYK value. So I tried using the ICC profiles. I believe this is the way PS (and others) are converting color spaces in their programs.  However I can't seem to get the same result. I think I'm missing something but I'm not sure what.
    The main API for making this pixel transformation, whether it be CreateMultiProfileTransform or CMCreateMultiProfileTransform, takes a list of color profiles. I tried using two profiles; the 'sRGB Color Space Profile.icm' and the 'USWebCoatedSWOP.icc' profile. However I'm not sure if there has to be some more profile in between !??
    In PS you can change the color space using the Windows ICM which is exactly the API's that I'm using, I just can't figure out what's wrong. Hope it's a fundamental error.
    I emailed the code to you so you can have a look. (you will need to install .NET framework 3.0 to get TiffBitmapEncoder working)

    • Edited by Calle Mellergardh Monday, July 7, 2008 9:34 PM And suddenly ____ was bad language...
    Monday, July 7, 2008 9:32 PM
  • Calle,
    Thank you  for trying. I will look at your code.

    I pulled down Paint.NET and played with their source. I didn't see anything CMYK in their code. I don't know if you have ever used their product, but it is a great free paint replacement and includes source. Highly recommended example of shrink-wrap.

    I did find these articles which I am reviewing:

    Finally, I do own John Miano's book called Compressed Image File Formats which describes the math. (Strange what books I buy and never read until years later.)

    Thanks again for your code; I will look at that and the resources I mentioned. I think there is probably an answer somewhere in here. If you are on Linked In, please link up with me:

    Tuesday, July 8, 2008 12:14 AM
  • Well, I haven't seen the Paint.NET up until now actually. Seems like a decent collection of code. However, at first glance they seem to be using the built in functionality of the Bitmap class to save the images. And as far I know theres no way of controlling the encoder to use any other color space than RGB.

    The article on code project was interesting, however I found no cmyk routines in the source code there as well.

    The link you posted points to the easy math for converting between RGB and CMYK. Im afraid it doesn't work. In the code I mailed to you there are older tests that uses this algorithm, and the results was not even near the Win ICM tests.

    I'm not on Linked In, perhaps I should concider it !?

    Tuesday, July 8, 2008 12:53 AM
  •  LinkedIn is a great networking site for developers, etc. Since it is free, it can't hurt!

    Thanks again!
    Tuesday, July 8, 2008 12:56 AM
  • Have you tried the example in the documentation for the "FormatConvertedBitmap Class" with PixelFormats.Cmyk32?
    Tuesday, July 8, 2008 2:17 PM
  • That sounds interesting. At first glance it looks promising, I will do some testing soon...

    Tuesday, July 8, 2008 8:23 PM
  • Now I've tested the FormatConvertedBitmap and ColorConvertedBitmap classes, here's my conclusion:

    First of all MSDN says to use the ColorConvertedBitmap for conversions between RGB and CMYK, however, according to the example, simply swithing to PixelFormats.Cmyk32 won't work since the ColorContext constructor does not support that pixelformat (why not?). I checked with Reflector and there's a whole bunch of formats that are not currently supported. However, there's another constructor that takes an Uri. I tried to specify an ICC color profile used for CMYK but then the ColorConvertedBitmap threw an exception. One might ask, why would MSDN says to should use the ColorConverted class for conversions between RGB and CMYK when it doesn't support it ?

    So I tried the FormatConvertedBitmap using the following code:  (sorry about the photo Mike ;)

                Stream imageStream = new FileStream(@"C:\temp\mike4.jpg",   
                        FileMode.Open, FileAccess.Read, FileShare.Read);  
                BitmapSource myBitmapSource = BitmapFrame.Create(imageStream);  
                FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();  
                newFormatedBitmapSource.Source = myBitmapSource;  
                newFormatedBitmapSource.DestinationFormat = PixelFormats.Cmyk32;  
                BitmapEncoder encoder = new TiffBitmapEncoder();  
                Stream cmykStream = new FileStream(@"C:\temp\mike4_CMYK.tif",   
                        FileMode.Create, FileAccess.Write, FileShare.Write);  

    This actually works (ta-da), I can open the new file in Photoshop and it recognizes it in a 8/CMYK format (Great). The colors are just ok, but then again the RGB->CMYK is not an easy conversion. And the printer may produce a better result out of it then my screen does.
    Unfortunatily, it's only the Tiff encoder that works. If I spcecify the Jpeg encoder PhotoShop no longer recognizes it as a 8/CMYK but as 8/RGB, same as original (some features not supported??).

    Using Reflector I can see that the ColorContext class relies heavily on the ICM API and this is used by both the Color- and FormatConvertedBitmap classes. However, the final touch seems to go through the WIC (Win Imaging Component) and it seem to be used slightly different by the two classes.

    Well, I think this is as far as I'll go with this. I would need to study the ICM and WIC some more to understand how they are used in this perticual case and there's just not enough time for everything...

    If the Tiff format is ok then the above code snippet would probably work for you, however, if the Jpeg is the desired output format, well, then we'll need some more input.

    Wednesday, July 9, 2008 8:00 PM
  • Hi!

    I didn't realize you beat me to the punch! I actually took a peek at the new direction John pointed us in, and I think I came to the same conclusion you did in a round-about way. It does work going to a TIFF:

    using System;

    using System.IO;

    using System.Windows.Forms;

    using System.Windows.Media;

    using System.Windows.Media.Imaging;


    namespace CMYK


        public partial class Form1 : Form


            public Form1()





            private void button1_Click(object sender, EventArgs e)


                BitmapImage rgb = new BitmapImage();

                string uri = "file:///" + Application.StartupPath.Replace(@"\", "/").Replace(" ", "%20") + "/Mike4.jpg";


                rgb.UriSource = new Uri(uri, UriKind.RelativeOrAbsolute);



                PixelFormat destinationFormat = new PixelFormat();

                BitmapPalette destinationPalette = new BitmapPalette(rgb, 255);

                int alphaThreshold = 0;

                FormatConvertedBitmap cmyk = new FormatConvertedBitmap(rgb, destinationFormat, destinationPalette, alphaThreshold);


                TiffBitmapEncoder tiff = new TiffBitmapEncoder();


                tiff.Compression = TiffCompressOption.Default;


                using (FileStream fs = new FileStream(Application.StartupPath + @"\Mike4.tiff", FileMode.Create, FileAccess.Write))







    Does the JpegBitmapEncoder jpg = new JpegBitmapEncoder(); support CMYK? There are a number of properties on that class, but I am out of my league and the online help is terse at best. I typically don't need to be this far in the bowels of .NET - ha!

    Thank you so much for all your help! I would not have thought to look in this direction...

    Thursday, July 10, 2008 2:31 AM
  • Sorry for beating you at the punch (thought I gave you enough time to put up a post... ;)

    Looking at your code I see that you are creating a new "blank" PixelFormat. That won't change the color space of the bitmap.
    Use PixelFormat destinationFormat = PixelFormats.Cmyk32; and you will see the change.

    Passing a destination palette looks like a nice twitch, however, I can't really see the change in the destination image (I changed to 16 colors which would radically decrease the quality of the Tiff image, but there was no change when I opened in PS.)
    I also tried setting the ColorContext property for both the frame being added to the encoder and for the encoder it self. With the TiffEncoder this works fine, but then again It wasn't neseccary since the FormatConvertedBitmap was all it took. The Jpeg encoder does however crasch when trying to save a frame that has the ColorContext set. And trying to set the ColorContext property of the JpegEncoder it self results in a "not spported" exception. It's clear that MS hasn't finished the JpegEncoder !!!

    It's been a lesson, thanks!, and good luck with the book publishing!


    Thursday, July 10, 2008 7:50 AM
  • What program will save a CMYK in a JPEG?  When did the JPEG specification change from 24 bit RGB?
    Thursday, July 10, 2008 8:38 AM
  • That's a good question John, I just assumed this was possible since Mike (thread owner) sent me a Jpeg image that when opened in PS had the CMYK/8 format. We all know it's possible to change the file extension but I belive that PS in this case is rather cranky about that and will not accept the file if the extension doesn't match the data in the file.
    However, I could be terribly wrong and mybe I was out of line giving MS a punch for not "finishing" the JpegEncoder.

    Perhaps I'm having trouble to understand the meaning of Pixel Format and Color Space. Are there any difference and how are they connected? If you know this John, would you like to share?

    Best regards

    Thursday, July 10, 2008 9:04 AM
  • John,
    Photoshop and Corel both convert between RGB and CMYK. Though I didn't convert the image via Photoshop CS3 personally, my son said it would convert fine. However, I did use Corel Photo Paint X3 to take my RGB JPEG and make it a CMYK JPEG. I use it's Document Properties on each image just to verify the conversion. It tells me everything I want to know about the image (compression, format, etc.) I have taken these files and tested them inside my book publishing no problem.

    It was Corel that gave me idea that this was possible. Their conversion actually looks good. That is why I thought to myself "surely Microsoft would have built this in the framework by now." -- ha!



    I was not clear exactly how to handle the PixelFormat. However, passing in a "blank" value does work. The TIFF looked fine to me.

    Regarding the supported number of colors, I tried Int32.Max which bombed. I put in 255 as an arbitrary value. Not sure what the max could be, but I figured 255 was safe.

    I discovered the destination pallette by playing with the method call. It was a guess and I didn't want to learn how to create my own pallette - ha!

    Since I don't have a background in image internals, a lot of these concepts (like frames) is alien to me. I did see a lot of blog entries suggesting that we are in an area of the framework that has not been completed at all. I think one thread from a team member said something like "this should have never been released in this state."

    Oh the joys of bleeding edge. It is not like I have not been here before with Microsoft for the past 20 years...

    I will continue experimenting...


    Thank you both for your replies!
    Thursday, July 10, 2008 9:38 AM
  • The advanced photograpic programs may have their own specification for their file extensions.  The color space for JPEG is YCbCr which can be linearly converted to RGB.  JPEG specifiction on the Adobe site.
    • Marked as answer by Zhi-Xin Ye Monday, July 14, 2008 2:27 AM
    Thursday, July 10, 2008 10:26 AM
  • JohnWein said:

    The advanced photograpic programs may have their own specification for their file extensions.  The color space for JPEG is YCbCr which can be linearly converted to RGB.  JPEG specifiction on the Adobe site.

    I'll rest my case...

    moflaherty said:

    I was not clear exactly how to handle the PixelFormat. However, passing in a "blank" value does work. The TIFF looked fine to me.

    When you open up the image in PS, did you look at the title bar? Did it say RGB/8 or CMYK/8. When I ran your code mine said RGB/8 which made me suspect that the format was never changed to Cmyk !?

    Thursday, July 10, 2008 11:13 AM
  • Why don't you guys find out the conversion matrix by observing the converted images of Photoshop and Corel?

    My experience told me it's tricky and the matrix printed on my textbook is not reliable (I attempted to convert the image from my camera..). It's better to find out by yourselves.. :)
    Friday, July 11, 2008 4:46 AM
  • The primary reason you are having trouble with this is that some of the RGB colors are outside the the color space of CMYK.
    If you have ever tried printing a RGB image on a CMYK color printer you will notice that the reds, the blues and the greens all appear somewhat more muddy than they do on the screen.  Even after a good conversion the image appears flat.

    RGB is additive and CMYK is subtractive color. If you you increase RGB to 255 for every value you get white on your monitor, if you do that with ink on paper you get mud.

    You have to first convert the RBG values to a CMY color space then convert that  into a CMYK Value. even with this basic conversion don't be surprised if the images look slightly different

    //RGB values from 0 to 255
    //CMY results from 0 to 1

    C = 1 - ( R / 255 )
    M = 1 - ( G / 255 )
    Y = 1 - ( B / 255 )

    //CMYK and CMY values from 0 to 1

    var_K = 1

    if ( C < var_K )   var_K = C
    if ( M < var_K )   var_K = M
    if ( Y < var_K )   var_K = Y
    if ( var_K == 1 ) { //Black
       C = 0
       M = 0
       Y = 0
    else {
    = ( C - var_K ) / ( 1 - var_K )
       M = ( M - var_K ) / ( 1 - var_K )
       Y = ( Y - var_K ) / ( 1 - var_K )
    K = var_K

    check out the site bleow for some good information on color space:
    Thursday, December 4, 2008 12:20 AM
  • I should mention that I changed the code in his article slightly:

    private bool IsCMYK(Image image)


                // based on


                bool ret = false ;




                    int cmyk = (image.Flags & (int )ImageFlags .ColorSpaceCmyk);

                    int ycck = (image.Flags & (int )ImageFlags .ColorSpaceYcck);


                    ret = ((cmyk > 0) || (ycck > 0));


                catch (Exception ex)


                    MessageBox .Show(this , ex.ToString(), App .Name, MessageBoxButtons .OK, MessageBoxIcon .Error);



                return ret;


    This code returns True in Vista, but False in Windows 7 (with the same image file offcourse)

    Any idea why this is happening and how to detect CMYK in Win7 ?

    I checked, and the image.Flags returns a different value in Vista and Windows7, no idea why.

    Any help would be much appreciated.


    Wednesday, August 4, 2010 7:22 AM
  • Apologies for necro'ing this thread..


    I had the same problem as all of the above, and I can't thank you guys enough for the RGB>CMYK conversion code for TIF's.  Exactly what I needed.


    In response to xyzzy_rad_eu's post regarding the snippet not working on Windows 7, and in case other's stumble across this thread, the below worked for me:


        Public Overloads Shared Function IsCmyk(ByVal imageStream As Stream) As Boolean
          If imageStream Is Nothing Then Throw New ArgumentNullException("imageStream")
          Dim decoder As BitmapDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default)
          Dim frame As BitmapFrame = decoder.Frames(0)
          Return frame.Format.Equals(System.Windows.Media.PixelFormats.Cmyk32)
        End Function


    I pinched this from a response on stackoverflow (thanks for the pointers Drew marsh):



    Thursday, April 14, 2011 12:21 PM