문제

I'm using PIL to process uploaded images. Unfortunately, I'm having trouble with color conversion from CMYK to RGB, as the resulting images tone and contrast changes.

I'd suspect that it's only doing direct number transformations. Does PIL, or anything built on top of it, have an Adobian dummy-proof consume embedded profile, convert to destination, preserve numbers tool I can use for conversion?

In all my healthy ignorance and inexperience, this sort of jumped at me and it's got me in a pinch. I'd really like to get this done without engaging any intricacies of color spaces, transformations and the necessary math for both at this point.

Though I've never previously used it, I'm also disposed at using ImageMagick for this processing step if anyone has experience that it can perform it in a gracious manner.

도움이 되었습니까?

해결책

So it didn't take me long to run into other people mentioning Little CMS, being the most popular open source solution for color management. I ended snooping around for Python bindings, found the old pyCMS and some ostensible notions about PIL supporting Little CMS.

Indeed, there is support for Little CMS, it's mentioned in a whole whopping one-liner:

CMS support: littleCMS (1.1.5 or later is recommended).

The documentation contains no references, no topical guides, Google didn't crawl out anything, their mailing list is closed... but digging through the source there's a PIL.ImageCms module that's well documented and get's the job done. Hope this saves someone from a messy internet excavation.

Goes off getting himself a cookie...

다른 팁

it's 2019 and things have changed. Your problem is significantly more complex than it may appear at first sight. The problem is, CMYK to RGB and RGB to CMYK is not a simple there and back. If e.g. you open an image in Photoshop and convert it there, this conversion has 2 additional parameters: source color profile and destination color profile. These change things greatly! For a typical use case, you would assume Adobe RGB 1998 on the RGB side and say Coated FOGRA 39 on the CMYK side. These two additional pieces of information clarify to the converter how to deal with the colors on input and output. What you need next is a transformation mechanism, Little CMS is in deed a great tool for this. It is MIT licensed and (after looking for solutions myself for a considerable time), I would recommend the following setup if you indeed do need a python way to transform colors:

  1. Python 3.X (necessary because of littlecms)
  2. pip install littlecms
  3. pip install Pillow

In littlecms' /tests folder you will find a great set of examples. I would allow myself a particular adaptation of one test. Before you get the code, please let me tell you something about those color profiles. On Windows, as is my case, you will find a set of files with an .icc extension in the folder C:\Windows\System32\spool\drivers\color where Windows stores it's color profiles. You can download other profiles from sites like https://www.adobe.com/support/downloads/iccprofiles/iccprofiles_win.html and install them on Windows simply by double-clicking the corresponding .icc file. The example I provide depends on such profile files, which Little CMS uses to do those magic color transforms. I work as a semi-professional graphics designer and needed to be able to convert colors from CMYK to RGB and vice versa for certain scripts that manipulate objects in InDesign. My setup is RGB: Adobe RGB 1998 and CMYK: Coated FOGRA 39 (these settings were recommended by most book printers I get my books printed at). The aforementioned color profiles generated very similar results for me to the same transforms made by Photoshop and InDesign. Still, be warned, the colors are slightly (by around 1%) off in comparison to what PS and Id will give you for the same inputs. I am trying to figure out why...

The little program:

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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top