Thread: [Lcms-user] ctypes Python bindings
An ICC-based CMM for color management
Brought to you by:
mm2
From: Guy K. K. <G....@ma...> - 2008-10-12 00:47:06
|
Hi, I'm currently working on Python bindings for liblcms using ctypes as I've outlined sometimes in the past (June, July) already. Actually, it's quite easy. The main bindings are all generated using Thomas Heller's code generator for ctypes in ctypeslib. So far I've got a generated wrapper working, that can already do all the stuff the official bindings can do. Although this has not been tested extensively. But previously unavailable bindings e. g. for float values of colour values, etc. are now also easily possible. Still need to do some things, but mainly to make things "nicer". The way it is currently structured: * _lcms.py (generated module with all bindings) * c_lcms.py (module to take some cleanups to make the bindings more Pythonic) * lcms.py (could also be called differently. But this is for an OO wrapped API that is to ease the use of LittleCMS under Python) Things still to do: * split up the code generation into three sub-modules: * LCMS base (API basics as version, ...; constants; flags for colour space type, rendering intent, ...) * LCMS types * LCMS functions Well, if anybody is interested. Give me a shout. The stuff is usable already, however the Python API and the module arrangement will still fluctuate a bit. One of the questions I'm having now is this: Where to host the code? Should it go into the original LittleCMS CVS repository, or somewhere else? Oh, and it works nicely together with numpy. I think Frédéric asked about that. So interaction with PIL images should be also quite easily done. Guy -- Guy K. Kloss Institute of Information and Mathematical Sciences Te Kura Pūtaiao o Mōhiohio me Pāngarau Room 2.63, Quad Block A Building Massey University, Auckland, Albany Private Bag 102 904, North Shore Mail Centre voice: +64 9 414-0800 ext. 9585 fax: +64 9 441-8181 eMail: G....@ma... http://www.massey.ac.nz/~gkloss/ |
From: Guy K. K. <G....@ma...> - 2008-10-15 05:59:25
|
Here's a simple example using my object oriented Python API for LCMS with the ctypes library bindings. It uses numpy arrays as the native input/output data type of the buffers for conversion. The conversion uses a profile in the file system for input, and a built in profile for output. Here I'm converting 8 bit RGB to floating point Lab. So far I have added the basics for profile and transformation handling, and I've tried to automate as many things as possible to make it as easy to use, but with the option pass certain features in as optional parameters if needed. Transformations work e. g. with and without given output buffer. This way in-place transformations, or transformations into existing buffers (or images) are possible, as well as in the example the creation of a new suitable matrix to store the values. Any comments? Guy import numpy import Lcms # Read an input profile from the file system. myProfile = Lcms.Profile('testdata/target.icc') # Create an LCMS default profile for Lab. labProfile = Lcms.Profile(colourSpace=Lcms.PT_Lab) # Create a transformation object for # device RGB (8 bit) -> Lab (double) conversion, # rendering intent INTENT_PERCEPTUAL. myTransform = Lcms.Transform(myProfile, labProfile, outputDepth=0) rgbColours = numpy.array([[159, 189, 63], [230, 162, 39]], dtype=numpy.uint8) # Transform just one colour tuple labColours = myTransform.doTransform(rgbColours) print 'RGB colours:\n', rgbColours print 'Lab colours:\n', labColours -- Guy K. Kloss Institute of Information and Mathematical Sciences Te Kura Pūtaiao o Mōhiohio me Pāngarau Room 2.63, Quad Block A Building Massey University, Auckland, Albany Private Bag 102 904, North Shore Mail Centre voice: +64 9 414-0800 ext. 9585 fax: +64 9 441-8181 eMail: G....@ma... http://www.massey.ac.nz/~gkloss/ |
From: Frédéric <fre...@gb...> - 2008-10-15 06:32:14
|
On mercredi 15 octobre 2008, Guy K. Kloss wrote: > Here's a simple example using my object oriented Python API for LCMS > with the ctypes library bindings. It uses numpy arrays as the native > input/output data type of the buffers for conversion. The conversion > uses a profile in the file system for input, and a built in profile for > output. Here I'm converting 8 bit RGB to floating point Lab. > > So far I have added the basics for profile and transformation handling, > and I've tried to automate as many things as possible to make it as easy > to use, but with the option pass certain features in as optional > parameters if needed. Transformations work e. g. with and without given > output buffer. This way in-place transformations, or transformations > into existing buffers (or images) are possible, as well as in the > example the creation of a new suitable matrix to store the values. > > Any comments? Sounds good :o) Can you show an example for a in-place transform? -- Frédéric http://www.gbiloba.org |
From: Guy K. K. <G....@ma...> - 2008-10-15 20:00:21
|
On Wed, 15 Oct 2008 7:31:49 pm Frédéric wrote: > Can you show an example for a in-place transform? Yes, of course. I have just renamed the OO module to "littlecms" as I figured there may be some possible clashes on Wintendo using just case as a difference for files. This way it may better co-exist with the "classic" LCMS SWIG bindings, too. Note that naming of the parameter (destinationBuffer) in the doTransform() call can also be omitted, as destination buffer is the second call parameter. I just slipped it in for the example as it makes it clearer what happens. The code sample below converts 8 bit device RGB to 8 bit sRGB: #!/usr/bin/env python import numpy import littlecms # Read an input profile from the file system. myProfile = littlecms.Profile('testdata/target.icc') # Create an LCMS default profile for Lab. srgbProfile = littlecms.Profile(colourSpace=littlecms.PT_RGB) # Create a transformation object for # device RGB (8 bit) -> Lab (double) conversion, # rendering intent INTENT_PERCEPTUAL. myTransform = littlecms.Transform(myProfile, srgbProfile) rgbColours = numpy.array([[159, 189, 63], [230, 162, 39]], dtype=numpy.uint8) print 'device RGB colours:\n', rgbColours # Transform just one colour tuple myTransform.doTransform(rgbColours, destinationBuffer=rgbColours) print 'sRGB colours:\n', rgbColours Guy -- Guy K. Kloss Institute of Information and Mathematical Sciences Te Kura Pūtaiao o Mōhiohio me Pāngarau Room 2.63, Quad Block A Building Massey University, Auckland, Albany Private Bag 102 904, North Shore Mail Centre voice: +64 9 414-0800 ext. 9585 fax: +64 9 441-8181 eMail: G....@ma... http://www.massey.ac.nz/~gkloss/ |
From: Frédéric <fre...@gb...> - 2008-10-15 20:35:08
|
On mercredi 15 octobre 2008, Guy K. Kloss wrote: > > Can you show an example for a in-place transform? > > Yes, of course. I have just renamed the OO module to "littlecms" as I > figured there may be some possible clashes on Wintendo using just case > as a difference for files. This way it may better co-exist with the > "classic" LCMS SWIG bindings, too. > > Note that naming of the parameter (destinationBuffer) in the > doTransform() call can also be omitted, as destination buffer is the > second call parameter. I just slipped it in for the example as it makes > it clearer what happens. The code sample below converts 8 bit device RGB > to 8 bit sRGB: Ok, I see. Is there any gain (speed ?) to do in-place transform, instead of doing : rgbColours = myTransform.doTransform(rgbColours) -- Frédéric http://www.gbiloba.org |
From: Guy K. K. <G....@ma...> - 2008-10-25 20:35:06
|
I was still short an example on how to do a colour transformation using PIL and numpy with the new LittleCMS bindings: import numpy from PIL import Image import littlecms # Read an input profile from the file system. inProfile = littlecms.Profile('../testdata/target.icc') # Create an LCMS default profile for sRGB. outProfile = littlecms.Profile(colourSpace=littlecms.PT_RGB) # Create a transformation object for # device RGB (8 bit) -> sRGB (8 bit) conversion, # rendering intent: INTENT_PERCEPTUAL. myTransform = littlecms.Transform(inProfile, outProfile) # Load the image and convert it to a numpy array. inImage = Image.open('myImage.png') inArray = numpy.asarray(inImage) # This one will take our output. outArray = numpy.zeros(inArray.shape, inArray.dtype) # Transform the image array line by line. for i in xrange(inArray.shape[0]): myTransform.doTransform(inArray[i], outArray[i]) # Create an image again of the output and save it. outImage = Image.fromarray(outArray) outImage.save('output.png') -- Guy K. Kloss Institute of Information and Mathematical Sciences Te Kura Pūtaiao o Mōhiohio me Pāngarau Room 2.63, Quad Block A Building Massey University, Auckland, Albany Private Bag 102 904, North Shore Mail Centre voice: +64 9 414-0800 ext. 9585 fax: +64 9 441-8181 eMail: G....@ma... http://www.massey.ac.nz/~gkloss/ |
From: Guy K. K. <G....@ma...> - 2008-10-15 20:57:39
|
On Thu, 16 Oct 2008 9:34:48 am Frédéric wrote: > Ok, I see. Is there any gain (speed ?) to do in-place transform, instead of > doing : > > rgbColours = myTransform.doTransform(rgbColours) Well, of course. Don't know how big it is, but it is present. Generally the doTransform() method calls internally the lcms cmsDoTransform() function, which *requires* an input and an output buffer, however both may be the same (for an in-place transform). The doTransform() method is implemented in a way that it allocates a buffer of adequate size, shape and type (using a numpy array) to hold the transformation results if you do not pass in an outputBuffer. So you will definitely have the overhead of the object creation (numpy.zeros() is called). If you do not want to perform an in-place transformation, but still don't want it to slow down, you can also create a suitable buffer yourself and use that one. To modify the in-place example to use a self created buffer just go as this: destinationColours = numpy.zeros(rgbColours.shape, dtype=numpy.uint8) myTransform.doTransform(rgbColours, destinationBuffer=destinationColours) This way they should be equally fast (without verification, but I doubt that cmsDoTransform() differs between two buffer and in-place transformations). Maybe I should package my stuff for others to give it a try. Guy -- Guy K. Kloss Institute of Information and Mathematical Sciences Te Kura Pūtaiao o Mōhiohio me Pāngarau Room 2.63, Quad Block A Building Massey University, Auckland, Albany Private Bag 102 904, North Shore Mail Centre voice: +64 9 414-0800 ext. 9585 fax: +64 9 441-8181 eMail: G....@ma... http://www.massey.ac.nz/~gkloss/ |
From: Guy K. K. <G....@ma...> - 2008-10-17 01:22:54
|
If someone wants to give it a spin, here's the current version of my bindings. On Thu, 16 Oct 2008 9:57:27 am Guy K. Kloss wrote: > Maybe I should package my stuff for others to give it a try. On my wiki I've placed a download link here: https://gutefee.massey.ac.nz/moin/GuyKloss The generated stubs are included. They're generated against lcms.h in version 1.1.6. The library they use is /usr/lib/liblcms.so.1. If it's also a version 1.1.6 on other linux distributions with the same path, then everything should work out of the box. Otherwise you may need to tinker a bit with the path. This library loading still needs to be made more robust, but I didn't worry about that much, yet, as I've got the generator. Documentation for the code is available in the sources using EpyDoc. Maybe I should've created the HTML docu from that already, but I didn't do that, yet. Whoever knows some python will be able to understand the code, I hope ... ;-) Guy -- Guy K. Kloss Institute of Information and Mathematical Sciences Te Kura Pūtaiao o Mōhiohio me Pāngarau Room 2.63, Quad Block A Building Massey University, Auckland, Albany Private Bag 102 904, North Shore Mail Centre voice: +64 9 414-0800 ext. 9585 fax: +64 9 441-8181 eMail: G....@ma... http://www.massey.ac.nz/~gkloss/ |