views:

2467

answers:

3

I want to do some image processing with OpenCV (in Python), but I have to start with a PIL Image object, so I can't use the cvLoadImage() call, since that takes a filename.

This recipe (adapted from http://opencv.willowgarage.com/wiki/PythonInterface) does not work because cvSetData complains argument 2 of type 'void *' . Any ideas?

from opencv.cv import *
from PIL import Image

pi = Image.open('foo.png')                       # PIL image
ci = cvCreateImage(pi.size, IPL_DEPTH_8U, 1)     # OpenCV image
data = pi.tostring()
cvSetData(ci, data, len(data))

I think the last argument to the cvSetData is wrong too, but I am not sure what it should be.

+5  A: 

The example you tried to adapt is for the new python interface for OpenCV 2.0. This is probably the source of the confusion between the prefixed and non-prefixed function names (cv.cvSetData() versus cv.SetData()).

OpenCV 2.0 now ships with two sets of python bindings:

  • The "old-style" python wrapper, a python package with the opencv.{cv,highgui,ml} modules
  • The new interface, a python C extension (cv.pyd), which wraps all the OpenCV functionalities (including the highgui and ml modules.)

The reason behind the error message is that the SWIG wrapper does not handle conversion from a python string to a plain-old C buffer. However, the SWIG wrapper comes with the opencv.adaptors module, which is designed to support conversions from numpy and PIL images to OpenCV.

The following (tested) code should solve your original problem (conversion from PIL to OpenCV), using the SWIG interface :

# PIL to OpenCV using the SWIG wrapper
from opencv import cv, adaptors, highgui
import PIL

pil_img = PIL.Image.open(filename)

cv_img = adaptors.PIL2Ipl(pil_img)

highgui.cvNamedWindow("pil2ipl")
highgui.cvShowImage("pil2ipl", cv_img)

However, this does not solve the fact that the cv.cvSetData() function will always fail (with the current SWIG wrapper implementation). You could then use the new-style wrapper, which allows you to use the cv.SetData() function as you would expect :

# PIL to OpenCV using the new wrapper
import cv
import PIL

pil_img = PIL.Image.open(filename)       

cv_img = cv.CreateImageHeader(pil_img.size, cv.IPL_DEPTH_8U, 3)  # RGB image
cv.SetData(cv_img, pil_img.tostring(), pil_img.size[0]*3)

cv.NamedWindow("pil2ipl")
cv.ShowImage("pil2ipl", cv_img)

A third approach would be to switch your OpenCV python interface to the ctypes-based wrapper. It comes with utility functions for explicit data conversion between e.g. python strings and C buffers. A quick look on google code search seems to indicate that this is a working method.

Concerning the third parameter of the cvSetData() function, size of the image buffer, but the image step. The step is the number of bytes in one row of your image, which is pixel_depth * number_of_channels * image_width. The pixel_depth parameter is the size in bytes of the data associated to one channel. In your example, it would be simply the image width (only one channel, one byte per pixel).

sevas
@sevas: I have not accepted your answer (yet), because I *am* using version 2.0 of OpenCV. The recipe on the page I linked to did not work at all until I changed cv.CreateImageHeader to cvCreateImage and cv.SetData to cvSetData, so I am still confused about that. I am going to try your approach with ctypes-opencv and if that works I will post my findings here.
scrible
@scrible: I added information about the two concurrent sets of bindings shipping with OpenCV 2.0. I will probably continue to look for a better solution though.
sevas
@scrible : I updated the answer using the latest information I could find (specifically, the adaptators module and the two sets of python bindings.)
sevas
@sevas: for the third argument, is there a necessity to be a multiple of 4? in this site: http://opencv.willowgarage.com/wiki/PythonInterface , there is written something like that: "We think that SetData must have a step that is a multiple of 4"
kolistivra
A: 

It's really confusing to have both swig and new python binding. For example, in the OpenCV 2.0, cmake can accepts both BUILD_SWIG_PYTHON_SUPPORT and BUILD_NEW_PYTHON_SUPPORT. But anyway, I kinda figured out most pitfalls.

In the case of using "import cv" (the new python binding), one more step is needed.

cv.SetData(cv_img, pil_img.tostring(), pil_img.size[0]*3)
cv.CvtColor(cv_img, cv_img, cv.CV_RGB2BGR)

The conversion is necessary for RGB images because the sequence is different in PIL and IplImage. The same applies to Ipl to PIL.

But if you use opencv.adaptors, it's already taken care of. You can look into the details in adaptors.py if interested.

Dingle
A: 

I did this using the python2.6 bindings of OpenCV2.1:

    ...
    cv_img = cv.CreateImageHeader(img.size, cv.IPL_DEPTH_8U, 3)
    cv.SetData(cv_img, img.rotate(180).tostring()[::-1])
    ...

The image rotation and reversion of the string is to swap RGB into BGR, that is used in OpenCV video encoding. I assume that this would also be necessary for any other use of an image converted from PIL to OpenCV.

Martin