Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Last active May 20, 2020 22:47
Show Gist options
  • Save mattdesl/3f6c7e9acad68927b18c749e2e91c857 to your computer and use it in GitHub Desktop.
Save mattdesl/3f6c7e9acad68927b18c749e2e91c857 to your computer and use it in GitHub Desktop.

convert CMYK to RGB with better perceptual accuracy

Using ICC profiles to match the same color conversions Photoshop uses.

First:

pip3 install littlecms

Then set up the files in a folder structure with __init__ and:

python3 convert.py
import littlecms as lc
PERCEPTUAL = lc.INTENT_PERCEPTUAL
RELATIVE_COLORIMETRIC = lc.INTENT_RELATIVE_COLORIMETRIC
SATURATION = lc.INTENT_SATURATION
ABSOLUTE_COLORIMETRIC = lc.INTENT_ABSOLUTE_COLORIMETRIC
# Modified from the following:
# https://stackoverflow.com/a/56621786/2297141
def rgb2lab(in_val, profile_rgb=None, intent=RELATIVE_COLORIMETRIC) :
white = lc.cmsD50_xyY() # Set white point for D50
if profile_rgb != None:
prgb = lc.cmsOpenProfileFromFile(profile_rgb, 'r') # cmsCreate_sRGBProfile()
else:
prgb = lc.cmsCreate_sRGBProfile()
plab = lc.cmsCreateLab4Profile(white)
transform = lc.cmsCreateTransform(prgb, lc.TYPE_RGB_8, plab, lc.TYPE_Lab_DBL,
intent,
lc.cmsFLAGS_NOCACHE|lc.cmsFLAGS_NOOPTIMIZE|lc.cmsFLAGS_BLACKPOINTCOMPENSATION|lc.cmsFLAGS_HIGHRESPRECALC)
n_pixels = 1
in_comps = 3
out_comps = 3
buf_in = lc.uint8Array(in_comps * n_pixels)
buf_out = lc.doubleArray(out_comps * n_pixels)
for i in range(in_comps):
buf_in[i] = in_val[i]
lc.cmsDoTransform(transform, buf_in, buf_out, n_pixels)
lc.cmsCloseProfile(prgb)
lc.cmsCloseProfile(plab)
lc.cmsDeleteTransform(transform)
return tuple(buf_out[i] for i in range(out_comps * n_pixels))
def lab2rgb(in_val, profile_rgb=None, intent=RELATIVE_COLORIMETRIC) :
white = lc.cmsD50_xyY() # Set white point for D50
if profile_rgb != None:
prgb = lc.cmsOpenProfileFromFile(profile_rgb, 'r')
else:
prgb = lc.cmsCreate_sRGBProfile()
plab = lc.cmsCreateLab4Profile(white)
transform = lc.cmsCreateTransform(plab, lc.TYPE_Lab_DBL, prgb, lc.TYPE_RGB_8,
intent,
lc.cmsFLAGS_NOCACHE|lc.cmsFLAGS_NOOPTIMIZE|lc.cmsFLAGS_BLACKPOINTCOMPENSATION|lc.cmsFLAGS_HIGHRESPRECALC)
n_pixels = 1
in_comps = 3
out_comps = 3
buf_in = lc.doubleArray(in_comps * n_pixels)
buf_out = lc.uint8Array(out_comps * n_pixels)
for i in range(in_comps):
buf_in[i] = in_val[i]
lc.cmsDoTransform(transform, buf_in, buf_out, n_pixels)
lc.cmsCloseProfile(prgb)
lc.cmsCloseProfile(plab)
lc.cmsDeleteTransform(transform)
return tuple(buf_out[i] for i in range(out_comps * n_pixels))
def cmyk2lab(in_val, profile_cmyk, intent=RELATIVE_COLORIMETRIC) :
white = lc.cmsD50_xyY() # Set white point for D50
plab = lc.cmsCreateLab4Profile(white)
pcmyk = lc.cmsOpenProfileFromFile(profile_cmyk, 'r')
transform = lc.cmsCreateTransform(pcmyk, lc.TYPE_CMYK_8, plab, lc.TYPE_Lab_DBL,
intent,
lc.cmsFLAGS_NOCACHE|lc.cmsFLAGS_NOOPTIMIZE|lc.cmsFLAGS_BLACKPOINTCOMPENSATION|lc.cmsFLAGS_HIGHRESPRECALC)
n_pixels = 1
in_comps = 4
out_comps = 3
buf_in = lc.uint8Array(in_comps * n_pixels)
buf_out = lc.doubleArray(out_comps * n_pixels)
for i in range(in_comps):
buf_in[i] = max(0, min(255, round(in_val[i] / 100 * 255)))
lc.cmsDoTransform(transform, buf_in, buf_out, n_pixels)
lc.cmsCloseProfile(plab)
lc.cmsCloseProfile(pcmyk)
lc.cmsDeleteTransform(transform)
return tuple(buf_out[i] for i in range(out_comps * n_pixels))
def lab2cmyk(in_val, profile_cmyk, intent=RELATIVE_COLORIMETRIC) :
white = lc.cmsD50_xyY() # Set white point for D50
plab = lc.cmsCreateLab4Profile(white)
pcmyk = lc.cmsOpenProfileFromFile(profile_cmyk, 'r')
transform = lc.cmsCreateTransform(plab, lc.TYPE_Lab_DBL, pcmyk, lc.TYPE_CMYK_8,
intent,
lc.cmsFLAGS_NOCACHE|lc.cmsFLAGS_NOOPTIMIZE|lc.cmsFLAGS_BLACKPOINTCOMPENSATION|lc.cmsFLAGS_HIGHRESPRECALC)
n_pixels = 1
in_comps = 3
out_comps = 4
buf_in = lc.doubleArray(in_comps * n_pixels)
buf_out = lc.uint8Array(out_comps * n_pixels)
for i in range(in_comps):
buf_in[i] = in_val[i]
lc.cmsDoTransform(transform, buf_in, buf_out, n_pixels)
lc.cmsCloseProfile(plab)
lc.cmsCloseProfile(pcmyk)
lc.cmsDeleteTransform(transform)
return tuple(round(buf_out[i] / 255 * 100) for i in range(out_comps * n_pixels))
def rgb2cmyk(in_val, profile_rgb=None, profile_cmyk=None, intent=RELATIVE_COLORIMETRIC) :
if profile_rgb != None:
prgb = lc.cmsOpenProfileFromFile(profile_rgb, 'r') # cmsCreate_sRGBProfile()
else:
prgb = lc.cmsCreate_sRGBProfile()
pcmyk = lc.cmsOpenProfileFromFile(profile_cmyk, 'r')
transform = lc.cmsCreateTransform(prgb, lc.TYPE_RGB_8, pcmyk, lc.TYPE_CMYK_8,
intent,
lc.cmsFLAGS_NOCACHE|lc.cmsFLAGS_NOOPTIMIZE|lc.cmsFLAGS_BLACKPOINTCOMPENSATION|lc.cmsFLAGS_HIGHRESPRECALC)
n_pixels = 1
in_comps = 3
out_comps = 4
buf_in = lc.uint8Array(in_comps * n_pixels)
buf_out = lc.uint8Array(out_comps * n_pixels)
for i in range(in_comps):
buf_in[i] = in_val[i]
lc.cmsDoTransform(transform, buf_in, buf_out, n_pixels)
lc.cmsCloseProfile(prgb)
lc.cmsCloseProfile(pcmyk)
lc.cmsDeleteTransform(transform)
return tuple(round(buf_out[i] / 255 * 100) for i in range(out_comps * n_pixels))
def cmyk2rgb(in_val, profile_rgb=None, profile_cmyk=None, intent=RELATIVE_COLORIMETRIC) :
if profile_rgb != None:
prgb = lc.cmsOpenProfileFromFile(profile_rgb, 'r') # cmsCreate_sRGBProfile()
else:
prgb = lc.cmsCreate_sRGBProfile()
pcmyk = lc.cmsOpenProfileFromFile(profile_cmyk, 'r')
transform = lc.cmsCreateTransform(pcmyk, lc.TYPE_CMYK_8, prgb, lc.TYPE_RGB_8,
intent,
lc.cmsFLAGS_NOCACHE|lc.cmsFLAGS_NOOPTIMIZE|lc.cmsFLAGS_BLACKPOINTCOMPENSATION|lc.cmsFLAGS_HIGHRESPRECALC)
n_pixels = 1
in_comps = 4
out_comps = 3
buf_in = lc.uint8Array(in_comps * n_pixels)
buf_out = lc.uint8Array(out_comps * n_pixels)
for i in range(in_comps):
buf_in[i] = max(0, min(255, round(in_val[i] / 100 * 255)))
lc.cmsDoTransform(transform, buf_in, buf_out, n_pixels)
lc.cmsCloseProfile(prgb)
lc.cmsCloseProfile(pcmyk)
lc.cmsDeleteTransform(transform)
return tuple(buf_out[i] for i in range(out_comps * n_pixels))
import cmyk_icc as icc
iccCMYK = 'data/icc/CMYK/U.S. Web Coated (SWOP) v2.icc'
iccRGB = 'data/icc/RGB/sRGB IEC61966-2.1.icc'
cmyk = [ 0, 35, 15, 0 ]
intent = icc.RELATIVE_COLORIMETRIC
rgb = icc.cmyk2rgb(cmyk, profile_rgb=iccRGB, profile_cmyk=iccCMYK, intent=intent)
lab = icc.cmyk2lab(cmyk, profile_cmyk=iccCMYK, intent=intent)
print('cmyk', cmyk)
print('rgb', rgb)
print('lab', lab)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment