Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# convert RGB value to CMYK using an ICC profile?

this question seems posted at many places over the interwebs and SO, but I could not find a satisfactory answer :(

How can I convert a RGB value to a CMYK value using an ICC profile?

The closest answer I have is there, where it explains how to convert from CMYK to RGB but not the other way around, which is what I need. (http://stackoverflow.com/questions/4920482/cmyk-to-rgb-formula-of-photoshop/5076731#5076731)

float[] colorValues = new float[4];
colorValues[0] = c / 255f;
colorValues[1] = m / 255f;
colorValues[2] = y / 255f;
colorValues[3] = k / 255f;

System.Windows.Media.Color color = Color.FromValues(colorValues,
new Uri(@"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc"));
System.Drawing.Color rgbColor = System.Drawing.Color.FromArgb(color.R, color.G, color.B);

I guess I should be using some classes/structures/methods from the System.Windows.Media namespace.

The System.Windows.Media.Color structure contains a method FromRgb, but I can't get CMYK values after, out of that System.Windows.Media.Color!

Many thanks

like image 391
Bruno Avatar asked Mar 08 '11 19:03

Bruno


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


2 Answers

None of the answers here seem to satisfactorily address the need to use an ICC profile.

I found an MS Connect issue page that has some sample code using Windows Imaging Components to convert an RBG JPEG to CMYK using an ICC profile.

If you have your ICC file and a sample JPEG file, you can set-up a console app to use this code very quickly.

I have saved the ICC profile in a folder called "Profiles" and have set the "Copy to Output Directory" value to "Always".

The JPEG is saved to a folder called "Images", and I have set its "Build Action" value to "Embedded Resource".

The console app project needs references to the following modules:

  • PresentationCore
  • System.Xaml
  • WindowsBase

The console app in full (named CMYKConversion) :

Program.cs:

using System;

namespace CMYKConversion
{
    class Program
    {
        static void Main(string[] args)
        {
            Converter c = new Converter();
            c.Convert();

            Console.ReadKey();
        }
    }
}

Converter.cs:

using System;
using System.IO;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace CMYKConversion
{
    public class Converter
    {
        public void Convert()
        {
            var rgbJpeg = BitmapFrame.Create(GetStreamFromResource("CMYKConversion.Images.Desert.jpg"));
            var iccCmykJpeg = new ColorConvertedBitmap(
                rgbJpeg,
                new ColorContext(PixelFormats.Default),
                new ColorContext(GetProfilePath("Profiles/1010_ISO_Coated_39L.icc")),
                PixelFormats.Cmyk32
                );
            var jpegBitmapEncoder = new JpegBitmapEncoder();
            jpegBitmapEncoder.Frames.Add(BitmapFrame.Create(iccCmykJpeg));
            var iccCmykJpegStream = new MemoryStream();
            jpegBitmapEncoder.Save(iccCmykJpegStream);

            iccCmykJpegStream.Flush();
            SaveMemoryStream(iccCmykJpegStream, "C:\\desertCMYK.jpg");
            iccCmykJpegStream.Close();
        }

        private Stream GetStreamFromResource(string name)
        {
            return typeof(Program).Assembly.GetManifestResourceStream(name);
        }

        private Uri GetProfilePath(string name)
        {
            string folder = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Program)).CodeBase);
            return new Uri(Path.Combine(folder, name));
        }

        private void SaveMemoryStream(MemoryStream ms, string fileName)
        {
            FileStream outStream = File.OpenWrite(fileName);
            ms.WriteTo(outStream);
            outStream.Flush();
            outStream.Close();
        }
    }
}
like image 41
Paul Suart Avatar answered Oct 25 '22 18:10

Paul Suart


I don't know of any C# API or library that can achieve this. However, if you have enough C/C++ knowledge to build a wrapper for C#, I see two options:

  • Windows Color System (WCS) (part of Windows)
  • LittleCMS

The System.Windows.Media namespace is very limited. There's probably a powerful engine (WCS?) behind it, but just a small part is made available.

Update:

Here's some C# code to do the conversion using WCS. It certainly could use a wrapper that would make it easier to use:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ICM
{
    public class WindowsColorSystem
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public class ProfileFilename
        {
            public uint type;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string profileData;
            public uint dataSize;

            public ProfileFilename(string filename)
            {
                type = ProfileFilenameType;
                profileData = filename;
                dataSize = (uint)filename.Length * 2 + 2;
            }
        };

        public const uint ProfileFilenameType = 1;
        public const uint ProfileMembufferType = 2;

        public const uint ProfileRead = 1;
        public const uint ProfileReadWrite = 2;


        public enum FileShare : uint
        {
            Read = 1,
            Write = 2,
            Delete = 4
        };

        public enum CreateDisposition : uint
        {
            CreateNew = 1,
            CreateAlways = 2,
            OpenExisting = 3,
            OpenAlways = 4,
            TruncateExisting = 5
        };

        public enum LogicalColorSpace : uint
        {
            CalibratedRGB = 0x00000000,
            sRGB = 0x73524742,
            WindowsColorSpace = 0x57696E20
        };

        public enum ColorTransformMode : uint
        {
            ProofMode = 0x00000001,
            NormalMode = 0x00000002,
            BestMode = 0x00000003,
            EnableGamutChecking = 0x00010000,
            UseRelativeColorimetric = 0x00020000,
            FastTranslate = 0x00040000,
            PreserveBlack = 0x00100000,
            WCSAlways = 0x00200000
        };


        enum ColorType : int
        {
            Gray = 1,
            RGB = 2,
            XYZ = 3,
            Yxy = 4,
            Lab = 5,
            _3_Channel = 6,
            CMYK = 7,
            _5_Channel = 8,
            _6_Channel = 9,
            _7_Channel = 10,
            _8_Channel = 11,
            Named = 12
        };


        public const uint IntentPerceptual = 0;
        public const uint IntentRelativeColorimetric = 1;
        public const uint IntentSaturation = 2;
        public const uint IntentAbsoluteColorimetric = 3;

        public const uint IndexDontCare = 0;


        [StructLayout(LayoutKind.Sequential)]
        public struct RGBColor
        {
            public ushort red;
            public ushort green;
            public ushort blue;
            public ushort pad;
        };

        [StructLayout(LayoutKind.Sequential)]
        public struct CMYKColor
        {
            public ushort cyan;
            public ushort magenta;
            public ushort yellow;
            public ushort black;
        };

        [DllImport("mscms.dll", SetLastError = true, EntryPoint = "OpenColorProfileW", CallingConvention = CallingConvention.Winapi)]
        static extern IntPtr OpenColorProfile(
            [MarshalAs(UnmanagedType.LPStruct)] ProfileFilename profile,
            uint desiredAccess,
            FileShare shareMode,
            CreateDisposition creationMode);

        [DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        static extern bool CloseColorProfile(IntPtr hProfile);

        [DllImport("mscms.dll", SetLastError = true, EntryPoint = "GetStandardColorSpaceProfileW", CallingConvention = CallingConvention.Winapi)]
        static extern bool GetStandardColorSpaceProfile(
            uint machineName,
            LogicalColorSpace profileID,
            [MarshalAs(UnmanagedType.LPTStr), In, Out] StringBuilder profileName,
            ref uint size);

        [DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        static extern IntPtr CreateMultiProfileTransform(
            [In] IntPtr[] profiles,
            uint nProfiles,
            [In] uint[] intents,
            uint nIntents,
            ColorTransformMode flags,
            uint indexPreferredCMM);

        [DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        static extern bool DeleteColorTransform(IntPtr hTransform);

        [DllImport("mscms.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        static extern bool TranslateColors(
            IntPtr hColorTransform,
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In] RGBColor[] inputColors,
            uint nColors,
            ColorType ctInput,
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] CMYKColor[] outputColors,
            ColorType ctOutput);



        public static void Test()
        {
            bool success;

            StringBuilder profileName = new StringBuilder(256);
            uint size = (uint)profileName.Capacity * 2;
            success = GetStandardColorSpaceProfile(0, LogicalColorSpace.sRGB, profileName, ref size);

            ProfileFilename sRGBFilename = new ProfileFilename(profileName.ToString());
            IntPtr hSRGBProfile = OpenColorProfile(sRGBFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);

            ProfileFilename isoCoatedFilename = new ProfileFilename(@"C:\Users\me\Documents\ISOcoated_v2_300_eci.icc");
            IntPtr hIsoCoatedProfile = OpenColorProfile(isoCoatedFilename, ProfileRead, FileShare.Read, CreateDisposition.OpenExisting);

            IntPtr[] profiles = new IntPtr[] { hSRGBProfile, hIsoCoatedProfile };
            uint[] intents = new uint[] { IntentPerceptual };
            IntPtr transform = CreateMultiProfileTransform(profiles, 2, intents, 1, ColorTransformMode.BestMode, IndexDontCare);

            RGBColor[] rgbColors = new RGBColor[1];
            rgbColors[0] = new RGBColor();
            CMYKColor[] cmykColors = new CMYKColor[1];
            cmykColors[0] = new CMYKColor();

            rgbColors[0].red = 30204;
            rgbColors[0].green = 4420;
            rgbColors[0].blue = 60300;

            success = TranslateColors(transform, rgbColors, 1, ColorType.RGB, cmykColors, ColorType.CMYK);

            success = DeleteColorTransform(transform);

            success = CloseColorProfile(hSRGBProfile);
            success = CloseColorProfile(hIsoCoatedProfile);
        }
    }
}
like image 70
Codo Avatar answered Oct 25 '22 18:10

Codo