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

发布于 2024-10-21 01:34:03 字数 790 浏览 6 评论 0原文

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

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

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

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

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

非常感谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

穿越时光隧道 2024-10-28 01:34:03

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

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

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:

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);
        }
    }
}
青巷忧颜 2024-10-28 01:34:03

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

我发现一个 MS Connect 问题页面有一些 使用 Windows 成像组件的示例代码,使用国际商会简介。

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

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

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

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

  • PresentationCore
  • System.Xaml
  • WindowsBase

完整的控制台应用程序(名为 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();
        }
    }
}

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();
        }
    }
}
是你 2024-10-28 01:34:03

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

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

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

According to an MVP GDI+ can read CMYK but can't encode it (Source: http://www.pcreview.co.uk/forums/convert-rgb-image-cmyk-t1419911.html). They go on to say that using TIF as an intermediation format may be the way to go.

Other than that, you might try Graphics Mill imaging SDK for .NET at http://imaging.aurigma.com/ (I'm not affiliated with this company).

I know this isn't much of an answer, but hopefully it sheds some light and points you in the right direction.

是你 2024-10-28 01:34:03

您可以看一下:将 RGB 颜色转换为 CMYK?

虽然此转换是相当主观,因此需要 ICC 配置文件,您可能可以从 ICC 中提取该“因素”并调整公式?

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

You could have a look at this: Convert RGB color to CMYK?

Although this conversion is fairly subjective, hence the need for the ICC profile, it may be that you can extract that "factor" from the ICC and adjust the formula?

What is the context is which you need to convert the RGB values to CMYK?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文