7

I'd like to convert a set of pixel values from one profiled colorspace to another, without these values residing in an image file, such as (say) a list of RGB/RGBA/CMYK/etc data structures.

I have Python and PIL at my disposal, but I'm interested in solutions in related environments if that's what it takes.

The latest PIL has very nice support for LittleCMS -- but no way to hand it anything other than a PIL image (or a legacy pyCMS object) for it to act upon.

As far as I can ascertain, the command-line tool icctrans that's included with LittleCMS does something of this sort, but I can't seem to find any non-skeletal documentation on it, and the documentation refers to it as a demonstration tool.

Tae-Sung Shin
  • 20,215
  • 33
  • 138
  • 240
fish2000
  • 4,289
  • 2
  • 37
  • 76

2 Answers2

7

In order to use the current 2.3 version of Little CMS with Python, I translated lcms2.h to lcms2consts.py with the h2py.py script that comes in the Python distribution. The script does not translate struct declarations, but the constants are enough to do basic color transformations with ctypes and lcms2 as a dynamic library.

This example transforms a single colour from double precision Lab to 8-bit sRGB using built-in profiles. Use cmsOpenProfileFromFile(filename, 'r') instead for files.

import ctypes
from ctypes import byref
from lcms2consts import *

lcms = ctypes.windll.lcms2

inprof = lcms.cmsCreateLab4Profile(0)
outprof = lcms.cmsCreate_sRGBProfile()
xform = lcms.cmsCreateTransform(inprof, TYPE_Lab_DBL, 
    outprof, TYPE_RGB_8,
    INTENT_PERCEPTUAL, 0)
lcms.cmsCloseProfile(inprof)
lcms.cmsCloseProfile(outprof)

DblTriplet = ctypes.c_double * 3
ByteTriplet = ctypes.c_ubyte * 3
inbuf = DblTriplet(60.1,20.2,0.5)
outbuf = ByteTriplet()
lcms.cmsDoTransform(xform, byref(inbuf), byref(outbuf), 1)
print list(outbuf)

lcms.cmsDeleteTransform(xform)
Janne Karila
  • 24,266
  • 6
  • 53
  • 94
  • I'd forgotten the question! That's a fantastic example, I found it quite useful (I ported it to cython to smooth out cross-platform dynamc-linking nits) but `h2py` is totally the Rosetta stone of Python extension programming. Thank you. – fish2000 Mar 02 '12 at 02:56
6

There are two ways.

  • The hack way: To reprofile N color structures (and/or transform them between colorspaces) you create a 1x(N+2) image with PIL.Image.new(), use yourimage.load() to get a pixel-setting object interface thing, and set values (0,0) through (0, N) to whatever you got. Set (0, N+1) to white and (0, N+2) to black, and transform (or proof-transform) that image using your favorite ICC files and PIL.ImageCms.ImageCmsTransform(). Blammo: that PIL object is now your LUT. Read the values off with the image.load() thingy and you're good.

  • The true-nerd way: You need to use Python-colormath -- which is great for colorspace transforms but not profiling. Colormath can't read ICC profiles, so either a) you get to parse their crazy binary format in a reliable way, or b) just do the math, literally. This guy Bruce Lindbloom has all the data available in excel format for, like, all of the matricies you need to reprofile your LUTs. He is totally awesome. I am still trying to 'just' feed this data into colormath, so yeah, that makes me less awesome as I am still trying to get this 'nerd way' into something resembling production-quality.

There you go. That is what I did so far to answer the question of freestanding, commando-style ICC LUT transforms. U guys, srsly.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
fish2000
  • 4,289
  • 2
  • 37
  • 76
  • ever get any further with python-colormath? – jedierikb Feb 16 '13 at 16:26
  • @jedierikb I actually did – while it doesn't allow for general ICC profile transforms, if you have a way of reading some of the basic profile information, `python-colormath` will cover 90% of the math. It's a bit of a counterintuitive way to go as if feels like you're special-casing everything, until you realize that "general" ICC usage is pretty much a special-case in and of its own right – most frequently your math involves an XYZ matrix transform to or from sRGB values, and using a LUT in most other cases. I'm reading the profile using the ICCProfile class from http://dispcalgui.hoech.net – fish2000 Feb 19 '13 at 00:38