C# 使用 ICC 配置文件将 RGB 值转换为 CMYK?

Posted

技术标签:

【中文标题】C# 使用 ICC 配置文件将 RGB 值转换为 CMYK?【英文标题】:C# convert RGB value to CMYK using an ICC profile? 【发布时间】:2011-07-11 08:40:38 【问题描述】:

这个问题似乎在互联网上的许多地方都发布过,但我找不到满意的答案:(

如何使用 ICC 配置文件将 RGB 值转换为 CMYK 值?

我得到的最接近的答案在那里,它解释了如何从 CMYK 转换为 RGB,但不是相反,这正是我所需要的。 (http://***.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);

我想我应该使用 System.Windows.Media 命名空间中的一些类/结构/方法。

System.Windows.Media.Color 结构包含一个 FromRgb 方法,但之后我无法从 System.Windows.Media.Color 中获取 CMYK 值!

非常感谢

【问题讨论】:

你见过这个吗? ***.com/questions/2426432/convert-rgb-color-to-cmyk 是的,但这似乎不正确,我需要使用 ICC 配置文件才能最精确,这种方法看起来像一个近似值,但谢谢! 我现在有了一个解决方案,可以使用 ICC 配置文件将 RGB 值转换为 CMYK - 如果您有兴趣,我会发布代码。 ibiza:你找到解决方案了吗?您能否告诉我您是否使用 ICC 配置文件将 RGB 转换为 CYMK。 【参考方案1】:

我不知道有任何 C# API 或库可以实现这一点。但是,如果您有足够的 C/C++ 知识来构建 C# 包装器,我会看到两个选项:

Windows Color System (WCS)(Windows 的一部分) LittleCMS

System.Windows.Media 命名空间非常有限。它背后可能有一个强大的引擎(WCS?),但只有一小部分可用。

更新:

这里有一些使用 WCS 进行转换的 C# 代码。它当然可以使用更易于使用的包装器:

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);
        
    

【讨论】:

Codo:RGB 值在 0-255 之间变化,但您提到了 RGB(30204, 4420, 60300);而对于 YCMK 值从 0 到 1 变化,但转换的结果非常大。请告诉我如何将此值转换为 0 -1。 输入和输出值的范围是 0 到 65535(无符号字)。因此,在您的情况下,您必须将输入值乘以 257(不是 256 或 255),然后将输出值除以 65535。 科多:感谢重播。应用您告诉的更改后。输出看起来不错。 只是为这个问题添加另一个解决方案,我今天(是的,3 年后)发现了这个(javascript)项目,它似乎能够在不同的配色方案之间进行转换: github.com/jiin/Rainbow Rainbow 使用张贴在网络上的 CMYK 公式,太差了,我无法理解它有什么用处。结果与真实颜色相差甚远。我想不出任何有帮助的情况。停止使用它。这没有任何意义。【参考方案2】:

这里的答案似乎都不能令​​人满意地解决使用 ICC 配置文件的需求。

我发现了一个 MS Connect 问题页面,其中包含一些 sample code using Windows Imaging Components 用于使用 ICC 配置文件将 RBG JPEG 转换为 CMYK。

如果您有 ICC 文件和示例 JPEG 文件,则可以设置控制台应用程序以非常快速地使用此代码。

我已将 ICC 配置文件保存在名为“Profiles”的文件夹中,并将“复制到输出目录”值设置为“始终”。

JPEG 保存到名为“Images”的文件夹中,我已将其“Build Action”值设置为“Embedded Resource”。

控制台应用项目需要引用以下模块:

PresentationCore System.Xaml WindowsBase

完整的控制台应用程序(名为 CMYKConversion):

程序.cs:

using System;

namespace CMYKConversion

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

            Console.ReadKey();
        
    

转换器.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();
        
    

【讨论】:

【参考方案3】:

根据 MVP GDI+ 可以读取 CMYK 但不能对其进行编码(来源:http://www.pcreview.co.uk/forums/convert-rgb-image-cmyk-t1419911.html)。他们接着说,使用 TIF 作为中介格式可能是可行的方法。

除此之外,您可以在http://imaging.aurigma.com/(我不隶属于这家公司)尝试适用于 .NET 的 Graphics Mill 成像 SDK。

我知道这不是一个很好的答案,但希望它能给你一些启发,并为你指明正确的方向。

【讨论】:

感谢光临,我看过 Graphics Mill 库,但我没有钱来咳咳…… 抱歉,我无法提供更多帮助。【参考方案4】:

你可以看看这个:Convert RGB color to CMYK?

虽然这种转换是相当主观的,因此需要 ICC 配置文件,也许您可​​以从 ICC 中提取那个“因素”并调整公式?

您需要将 RGB 值转换为 CMYK 的上下文是什么?

【讨论】:

嗨,马克,感谢您的帮助。我见过这种将 RGB 转换为 CMYK 的公式,但我真的更喜欢使用 ICC 配置文件,因为它比我的红色更精确。上下文是我正在生成一个图像文件,我只想使用与 CMYK 兼容的颜色,因为图像将被打印并且我不希望颜色失真,所以我想确保我使用的颜色将被准确识别就像 CMYK 打印机一样。我不确定我是否理解您的回答?亲切的问候 @ibiza,好的,在这种情况下,您可以做的是将图像创建为 RGB,然后使用 ImageMagick 转换为 CMYK(支持 ICC 配置文件)

以上是关于C# 使用 ICC 配置文件将 RGB 值转换为 CMYK?的主要内容,如果未能解决你的问题,请参考以下文章

icc文件作用是啥?

将 Rgb 图像转换为灰度 C# 代码时的性能问题

c# winform 如何实现16进制颜色值的转换

如何在OpenCV中使用Adobe RGB色彩空间读取jpeg图像?

在 Imagemagick 中转换图像时出错

从 24bpp 位图中获取每个像素的 RGB 值,以便在 C 中转换为 GBA 格式