You may have come across the following issue with OpenCV Python bindings:

image = cv.cvCreateImage(size, cv.IPL_DEPTH_8U, 3)
image.imageData = data # set some data

Trying to set the “data” of an OpenCV image directly fails. The problem is in the underlying SWIG code that attempts to make the imageData field available for writing at the python level. You can see in CVS that the code gets fixed immediately after the 1.0 release.

But wait! Doesn’t OpenCV come with an adaptors.py that is supposed to provide methods for translating between various Python data types (NumPy < => PIL < => IplImage) ?

A quick look at the code:

def PIL2Ipl(input):
    """Converts a PIL image to the OpenCV/IPL CvMat data format.
 
    Supported input image formats are:
        RGB
        L
        F
    """
 
    if not isinstance(input, PIL.Image.Image):
       raise TypeError, 'must be called with PIL.Image.Image!'
 
    size = cv.cvSize(input.size[0], input.size[1])
 
    # mode dictionary:
    # (pil_mode : (ipl_depth, ipl_channels, color model, channel Seq)
    mode_list = {
        "RGB" : (cv.IPL_DEPTH_8U, 3),
        "L"   : (cv.IPL_DEPTH_8U, 1),
        "F"   : (cv.IPL_DEPTH_32F, 1)
        }
 
    if not mode_list.has_key(input.mode):
        raise ValueError, 'unknown or unsupported input mode'
 
    modes = mode_list[input.mode]    
 
    result = cv.cvCreateImage(
        size,
        modes[0], # depth
        modes[1]  # channels
        )
 
    # set imageData
    result.imageData=input.tostring() #FAIL!
 
    return result

As you can see, adaptors.py tries to set imageData as well, and so fails to convert from any format into IplImage. Your options are to work from the repository version of OpenCV or to back port the change from the repository to your local installation. Both require compiling from source and so as you may imagine, neither case is convenient when working with libcv as installed from your favorite package manager of choice.

A third option is to back port the changes from the repository to your own custom swig module whose only goal is to implement a function that sets the imageData field properly. That’s the approach I took, and though it took a considerable amount of time to implement (because I don’t really understand SWIG), the end result did not require many lines of code. Here’s my iplimage.i file:

%module iplimage
%{
#include "cv.h"
void set(CvMat *self, char *string);
%}
 
void set(CvMat *self, char *string);

And here’s iplimage.c copied with minor changes directly out of the repository for OpenCV:

#include "cv.h"
 
void set(CvMat * self, char *string)
{
	int depth = CV_MAT_DEPTH(self-&gt;type);
	int cn = CV_MAT_CN(self-&gt;type);
	int step = self-&gt;step ? self-&gt;step : CV_ELEM_SIZE(self-&gt;type) * self-&gt;cols;
	long line;
	long pixel;
 
	if (depth == CV_8U &amp;&amp; cn==3){
		// RGB case
		// The data is reordered beause OpenCV uses BGR instead of RGB
 
		for (line = 0; line &lt; self-&gt;rows; ++line)
			for (pixel = 0; pixel &lt; self-&gt;cols; ++pixel)
			{
				// In OpenCV the beginning of the lines are aligned
				// to 4 Bytes. So use step instead of cols.
				long position = line*step + pixel*3;
				long sourcepos = line*self-&gt;cols*3 + pixel*3;
				self-&gt;data.ptr[position  ] = string[sourcepos+2];
				self-&gt;data.ptr[position+1] = string[sourcepos+1];
				self-&gt;data.ptr[position+2] = string[sourcepos  ];
			}
	}
	else if (depth == CV_8U &amp;&amp; cn==1)
	{
		// Grayscale 8bit case
 
		for (line = 0; line &lt; self-&gt;rows; ++line)
		{
			// In OpenCV the beginning of the lines are aligned
			// to 4 Bytes. So use step instead of cols.
			memcpy
				(
				 self-&gt;data.ptr + line*step,
				 string + line*self-&gt;cols,
				 step
				);
		}
	}
	else if ( depth == CV_32F )
	{
		// float (32bit) case
		for (line = 0; line &lt; self-&gt;rows; ++line)
		{
			// here we don not have to care about alignment as the Floats are
			// as long as the alignment
			memcpy
				(
				 self-&gt;data.ptr + line*step,
				 string + line*self-&gt;cols*sizeof(float),
				 step
				);
		}
	}
	else if ( depth == CV_64F )
	{
		// double (64bit) case
		for (line = 0; line &lt; self-&gt;rows; ++line)
		{
			// here we don not have to care about alignment as the Floats are
			// as long as the alignment
			memcpy
				(
				 self-&gt;data.ptr + line*step,
				 string + line*self-&gt;cols*sizeof(double),
				 step
				);
		}
	}
	else
	{
	}
}

An example makefile that works on my system:

CFLAGS= `pkg-config --cflags opencv`
LDFLAGS= `pkg-config --libs opencv` -L.
CXX=g++
 
_iplimage.so: iplimage.c
	swig -python iplimage.i
	$(CXX) $(CFLAGS) -I/usr/include/python2.5 -c iplimage.c iplimage_wrap.c
	$(CXX) $(LDFLAGS) -shared iplimage.o iplimage_wrap.o -o _iplimage.so

If you manage to build the module, you can then invoke it from python using a call to iplimage.set(image,data) to set the imageData field for real. You can use the default OpenCV Python bindings that come with your package manager and simply install this small additional module. Hopefully, we’ll see a new release of OpenCV soon that negates the need for any of these acrobatics.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Reddit
  • Technorati
  • Furl
  • StumbleUpon
  • Tumblr
  • TwitThis
12 Responses to “OpenCV and Python”
  1. Richard says:

    thanks for this. I was facing the same problem.

  2. Felicien says:

    Hello,

    I am using as well python (2.5) and opencv (1.0) but under windows XP. I try to set a face detector/histogram extractor for movies using pymedia and PIL.
    However, I’m having the same problem as you describe here: impossible to set data when using adaptors.py.
    So I have the following questions for you:
    - Would your method work under Windows?
    - Do you have any pieces of advice for me (knowing I’m actually quite a newbie and not really sure of what you explain here :) so that I can make the code work properly?

    Thanks a lot!

  3. JS says:

    I’m not sure. I don’t use Windows, but I imagine that there is an equivalent SWIG method for generating Python wrappers for Windows dlls.

  4. SinJax says:

    This was most helpful! Here the makefile had to be altered slightly, with the addition of the -fPIC to the compilation of _iplimage.o as follows:

    $(CXX) $(CFLAGS) -I/usr/include/python2.4 -fPIC -c iplimage.c iplimage_wrap.c

    Not sure why, google says this is due to some 64bit AMD architectures… but yes this is a good quick fix

    thanks :)

  5. Max M says:

    Hello,
    I managed to ‘make’ the code. It seems to have generated multiple files. Whe I run install it fails. Should I manually move these files to a specific locale in my ubuntu?
    Thanks…

  6. Andrew says:

    Hello,
    I’ve been able to build the files on Vista32 but when I try to convert from opencv to pil [iplimage.set(cvimg,pilimg.tostring())] I get a type error:

    TypeError: in method ’set’, argument 1 of type ‘CvMat *’. I check the type of cvimg and it is . Any clues on how to fix this?
    Thanks
    Andrew

  7. JS says:

    My skills with makefiles are primitive, and this code is not packaged in any way. You can copy it anywhere you would like, and as long as your Python path is setup to find it, the module should load.

  8. JS says:

    I assume you mean to convert pil to opencv. This is a weird error, considering that argument 1 is supposed to be of type CvMat *. I can’t really offer a solution, since I’m not sure what’s wrong. I’d follow the debugger into the iplimage.set code generated by SWIG and check to make sure that all the argument types are as expected. If you can’t find an error there, then you may have to fire up gdb (or whatever the Windows alternative debugger is). Attach to the running Python process and set a breakpoint in the SWIG generated _iplimage.so, then run your test case. You should be able to see what’s wrong from within the debugger.

  9. Carl says:

    You’re my hero… been struggling with this all day and your solution worked first time around. Thanks!

  10. diego diaz says:

    hi
    I have a similar problem. I think
    I’m trying to use a opencv python script to run my fireware webcam but I got this problem

    File “/var/lib/python-support/python2.5/opencv/adaptors.py”, line 79, in Ipl2PIL
    raise TypeError, ‘must be called with a cv.CvMat!’
    TypeError: must be called with a cv.CvMat!

    when I call opencv.adaptors.Ipl2PIL(im)
    do you think you method could work?

  11. JS says:

    Probably not. This method goes from pil to ipl. You seem to want to do the opposite.

  12. Zak Stone says:

    Tremendous thanks for explaining this issue in detail — the problem still seems to exist in Ubuntu’s most current version of OpenCV. With the addition of -fPIC after the python include statement in the makefile, I was able to get your code to compile on a 64-bit machine.

  13.  
Leave a Reply