在PIL中CMYK到RGB转换期间保留颜色

时间:2011-02-15 11:52:02

标签: python-imaging-library color-space

我正在使用PIL处理上传的图片。不幸的是,我在从CMYK到RGB的颜色转换方面遇到了麻烦,因为产生的图像色调和对比度发生了变化。

我怀疑它只进行直接数字转换。 PIL或其上构建的任何东西是否具有Adobian虚拟证明使用嵌入式配置文件,转换为目标,保留数字我可用于转换的工具?

在我所有健康的无知和缺乏经验中,这种对我的冲击,让我陷入困境。我真的很想完成这项工作,而不会在这一点上对任何复杂的色彩空间,变换和必要的数学进行处理。

虽然我之前从未使用过它,但如果有人有经验证明它可以以优雅的方式执行它,我也会使用ImageMagick进行此处理步骤。

3 个答案:

答案 0 :(得分:8)

所以我花了很长时间才与其他人提到Little CMS,这是最受欢迎的色彩管理开源解决方案。我结束了窥探Python绑定,找到了旧的pyCMS和一些关于PIL支持Little CMS的表面观念。

事实上,有人支持Little CMS,它在一个非常棒的单行中提到:

  

CMS支持:littleCMS(建议使用1.1.5或更高版本)。

该文档不包含任何引用,没有主题指南,Google没有抓取任何内容,他们的邮件列表已关闭......但是在源代码中挖掘出一个PIL.ImageCms模块,该模块已有详细记录并完成工作。希望这可以使某人免受凌乱的互联网挖掘。

让自己成为一个cookie ......

答案 1 :(得分:1)

这是2019年,情况已经改变。您的问题比乍看之下要复杂得多。问题是,从CMYK到RGB以及从RGB到CMYK的过程并不简单。如果例如您在Photoshop中打开图像并在那里进行转换,此转换还有2个附加参数:源颜色配置文件和目标颜色配置文件。这些改变很大!对于典型的用例,您将假设Adobe RGB 1998在RGB端,并说Coated FOGRA 39在CMYK端。这两个额外的信息向转换器阐明了如何处理输入和输出上的颜色。接下来需要的是一种转换机制,为此,Little CMS确实是一个很好的工具。它是MIT许可的产品((经过相当长一段时间自己寻找解决方案之后),如果您确实需要使用python方式转换颜色的话,我建议您进行以下设置:

  1. Python 3.X(由于littlecms而必需)
  2. pip install littlecms
  3. pip install Pillow

在littlecms的/tests文件夹中,您会找到很多示例。我会允许自己对一种测试进行特殊的修改。在获得代码之前,请让我告诉您一些有关这些颜色配置文件的信息。在Windows上,就像我的情况一样,您会在Windows存储其颜色配置文件的文件夹.icc中找到一组扩展名为C:\Windows\System32\spool\drivers\color的文件。您可以从https://www.adobe.com/support/downloads/iccprofiles/iccprofiles_win.html之类的站点下载其他配置文件,然后只需双击相应的.icc文件即可将其安装在Windows上。我提供的示例取决于此类配置文件,Little CMS使用这些配置文件来进行这些神奇的颜色转换。我是半专业的图形设计师,需要能够将某些颜色的字符从CMYK转换为RGB,反之亦然。我的设置是RGB:Adobe RGB 1998和CMYK:Coated FOGRA 39(这些设置是大多数书本打印机推荐的,我可以在此使用书本进行打印)。上面提到的颜色配置文件对我来说与Photoshop和InDesign进行的转换非常相似。不过请注意,与PS和Id为相同输入提供的颜色相比,颜色会稍微 (大约降低1%)。我试图找出原因...

小程序:

import littlecms as lc
from PIL import Image

def rgb2cmykColor(rgb, psrc='C:\\Windows\\System32\\spool\\drivers\\color\\AdobeRGB1998.icc', pdst='C:\\Windows\\System32\\spool\\drivers\\color\\CoatedFOGRA39.icc') :
    ctxt = lc.cmsCreateContext(None, None)

    white = lc.cmsD50_xyY()    # Set white point for D50
    dst_profile = lc.cmsOpenProfileFromFile(pdst, 'r')
    src_profile = lc.cmsOpenProfileFromFile(psrc, 'r') # cmsCreate_sRGBProfile()
    transform = lc.cmsCreateTransform(src_profile, lc.TYPE_RGB_8, dst_profile, lc.TYPE_CMYK_8,
                                  lc.INTENT_RELATIVE_COLORIMETRIC, lc.cmsFLAGS_NOCACHE)

    n_pixels = 1
    in_comps = 3
    out_comps = 4
    rgb_in = lc.uint8Array(in_comps * n_pixels)
    cmyk_out = lc.uint8Array(out_comps * n_pixels)
    for i in range(in_comps):
        rgb_in[i] = rgb[i]

    lc.cmsDoTransform(transform, rgb_in, cmyk_out, n_pixels)

    cmyk = tuple(cmyk_out[i] for i in range(out_comps * n_pixels))
    return cmyk

def cmyk2rgbColor(cmyk, psrc='C:\\Windows\\System32\\spool\\drivers\\color\\CoatedFOGRA39.icc', pdst='C:\\Windows\\System32\\spool\\drivers\\color\\AdobeRGB1998.icc') :
    ctxt = lc.cmsCreateContext(None, None)

    white = lc.cmsD50_xyY()    # Set white point for D50
    dst_profile = lc.cmsOpenProfileFromFile(pdst, 'r')
    src_profile = lc.cmsOpenProfileFromFile(psrc, 'r') # cmsCreate_sRGBProfile()
    transform = lc.cmsCreateTransform(src_profile, lc.TYPE_CMYK_8, dst_profile, lc.TYPE_RGB_8,
                                  lc.INTENT_RELATIVE_COLORIMETRIC, lc.cmsFLAGS_NOCACHE)

    n_pixels = 1
    in_comps = 4
    out_comps = 3
    cmyk_in = lc.uint8Array(in_comps * n_pixels)
    rgb_out = lc.uint8Array(out_comps * n_pixels)
    for i in range(in_comps):
        cmyk_in[i] = cmyk[i]

    lc.cmsDoTransform(transform, cmyk_in, rgb_out, n_pixels)

    rgb = tuple(rgb_out[i] for i in range(out_comps * n_pixels))
    return rgb

def rgb2cmykImage(PILImage, psrc='C:\\Windows\\System32\\spool\\drivers\\color\\AdobeRGB1998.icc', pdst='C:\\Windows\\System32\\spool\\drivers\\color\\CoatedFOGRA39.icc') :
    ctxt = lc.cmsCreateContext(None, None)
    white = lc.cmsD50_xyY()    # Set white point for D50
    dst_profile = lc.cmsOpenProfileFromFile(pdst, 'r')
    src_profile = lc.cmsOpenProfileFromFile(psrc, 'r')
    transform = lc.cmsCreateTransform(src_profile, lc.TYPE_RGB_8, dst_profile, lc.TYPE_CMYK_8,
                                  lc.INTENT_RELATIVE_COLORIMETRIC, lc.cmsFLAGS_NOCACHE)

    n_pixels = PILImage.size[0]

    in_comps = 3
    out_comps = 4
    n_rows = 16

    rgb_in = lc.uint8Array(in_comps * n_pixels * n_rows)
    cmyk_out = lc.uint8Array(out_comps * n_pixels * n_rows)

    outImage = Image.new('CMYK', PILImage.size, 'white')
    in_row = Image.new('RGB', (PILImage.size[0], n_rows), 'white')
    out_row = Image.new('CMYK', (PILImage.size[0], n_rows), 'white')
    out_b = bytearray(n_pixels * n_rows * out_comps)
    row = 0

    while row < PILImage.size[1] :

        in_row.paste(PILImage, (0, -row))
        data_in = in_row.tobytes('raw')

        j = in_comps * n_pixels * n_rows

        for i in range(j):
            rgb_in[i] = data_in[i]

        lc.cmsDoTransform(transform, rgb_in, cmyk_out, n_pixels * n_rows)

        for j in cmyk_out :
            out_b[j] = cmyk_out[j]

        out_row = Image.frombytes('CMYK', in_row.size, bytes(out_b))
        outImage.paste(out_row, (0, row))
        row += n_rows

    return outImage

def cmyk2rgbImage(PILImage, psrc='C:\\Windows\\System32\\spool\\drivers\\color\\CoatedFOGRA39.icc', pdst='C:\\Windows\\System32\\spool\\drivers\\color\\AdobeRGB1998.icc') :
    ctxt = lc.cmsCreateContext(None, None)
    white = lc.cmsD50_xyY()    # Set white point for D50
    dst_profile = lc.cmsOpenProfileFromFile(pdst, 'r')
    src_profile = lc.cmsOpenProfileFromFile(psrc, 'r')
    transform = lc.cmsCreateTransform(src_profile, lc.TYPE_CMYK_8, dst_profile, lc.TYPE_RGB_8,
                                  lc.INTENT_RELATIVE_COLORIMETRIC, lc.cmsFLAGS_NOCACHE)

    n_pixels = PILImage.size[0]

    in_comps = 4
    out_comps = 3
    n_rows = 16

    cmyk_in = lc.uint8Array(in_comps * n_pixels * n_rows)
    rgb_out = lc.uint8Array(out_comps * n_pixels * n_rows)

    outImage = Image.new('RGB', PILImage.size, 'white')
    in_row = Image.new('CMYK', (PILImage.size[0], n_rows), 'white')
    out_row = Image.new('RGB', (PILImage.size[0], n_rows), 'white')
    out_b = bytearray(n_pixels * n_rows * out_comps)
    row = 0

    while row < PILImage.size[1] :

        in_row.paste(PILImage, (0, -row))
        data_in = in_row.tobytes('raw')

        j = in_comps * n_pixels * n_rows

        for i in range(j):
            cmyk_in[i] = data_in[i]

        lc.cmsDoTransform(transform, cmyk_in, rgb_out, n_pixels * n_rows)

        for j in rgb_out :
            out_b[j] = rgb_out[j]

        out_row = Image.frombytes('RGB', in_row.size, bytes(out_b))
        outImage.paste(out_row, (0, row))
        row += n_rows

    return outImage

答案 2 :(得分:0)

实现此目的的任何人都需要注意的事情:您可能希望采用uint8 CMYK值(0-255)并将它们四舍五入为0-100范围,以更好地匹配大多数颜色选择器和这些值的使用。在这里查看我的代码:https://gist.github.com/mattdesl/ecf305c2f2b20672d682153a7ed0f133