如何从PIL图像创建OpenCV图像?

12

我想使用OpenCV(Python版)进行图像处理,但是我必须从一个PIL的 Image 对象开始,所以我不能使用 cvLoadImage()调用,因为它需要文件名。

这个食谱(改编自http://opencv.willowgarage.com/wiki/PythonInterface)不起作用,因为 cvSetData 抱怨类型为'void *'的第2个参数。有什么建议吗?

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)) 

我认为cvSetData的最后一个参数也是错误的,但我不确定应该是什么。


1
您在上面输入的代码中有一个未定义的名称(cvSetData),因此很难知道您是否真正尝试过它。 - Jonathan Feinberg
@Jonathan:我没有遇到你提到的错误。cvSetData确实是在opencv.cv中定义的有效函数。 - Arkady
答案目前的状态是什么? - Joseph Turian
4个回答

9
你尝试适应的示例是针对OpenCV 2.0的新Python接口的。这可能是前缀和非前缀函数名称(`cv.cvSetData()`与`cv.SetData()`)之间混淆的源头。
现在,OpenCV 2.0附带了两个Python绑定:
- 旧样式Python包装器,一个带有`opencv.{cv、highgui、ml}`模块的Python包。 - 新的接口,一个Python C扩展(`cv.pyd`),它包装了所有OpenCV功能(包括`highgui`和`ml`模块)。
错误消息背后的原因是SWIG包装器不处理将Python字符串转换为普通C缓冲区。然而,SWIG包装器附带了`opencv.adaptors`模块,旨在支持将`numpy`和`PIL`图像转换为OpenCV。
下面这段代码(已测试)应该可以解决您最初的问题(从 PIL 到 OpenCV 的转换),使用 SWIG 接口:
# 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)

然而,这并不能解决 cv.cvSetData() 函数始终会失败的问题(在当前的 SWIG 封装实现中)。 您可以使用新式封装器,它允许您像预期的那样使用 cv.SetData() 函数:
# 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)

一种方法是将OpenCV的Python接口切换到基于ctypes的包装器。它带有实用函数,用于显式数据转换,例如Python字符串和C缓冲区之间的转换。在Google代码搜索上快速查看似乎表明这是一种可行的方法。
关于cvSetData()函数的第三个参数,它表示图像缓冲区的大小,而不是图像步长。步长是图像一行中的字节数,即pixel_depth * number_of_channels * image_widthpixel_depth参数是与一个通道相关联的数据的字节数。在您的示例中,它只是图像宽度(仅一个通道,每个像素一个字节)。

@sevas:我还没有接受你的答案,因为我正在使用OpenCV 2.0版本。我链接到的页面上的方法根本不起作用,直到我将cv.CreateImageHeader更改为cvCreateImage,cv.SetData更改为cvSetData,所以我仍然感到困惑。我将尝试使用ctypes-opencv的方法,如果成功了,我会在这里发布我的发现。 - Arkady
@scrible:我已经添加了关于OpenCV 2.0同时存在的两组绑定的信息。尽管如此,我可能会继续寻找更好的解决方案。 - Frederic De Groef
@scrible:我使用最新的信息更新了答案(具体来说,是适配器模块和两组Python绑定)。 - Frederic De Groef
在第三个参数中,有必要是4的倍数吗?在这个网站上:http://opencv.willowgarage.com/wiki/PythonInterface,有类似这样的内容:“我们认为SetData必须有一个是4的倍数的步长”。 - kolistivra
我正在尝试使用您在此处展示的第一种方法,只是我将数字1作为CreateImageHeader()函数的第三个参数,并且在OpenCV窗口中显示的图像全部混乱。我也尝试保留参数3,但仍然是同样的结果。我的加载图片是PNG格式的。请问可能出了什么问题? - pkout

4

同时拥有swig和新的python绑定确实让人感到困惑。例如,在OpenCV 2.0中,cmake可以接受BUILD_SWIG_PYTHON_SUPPORT和BUILD_NEW_PYTHON_SUPPORT。但无论如何,我已经找出了大部分陷阱。

如果使用“import cv”(新的Python绑定),则需要再添加一步。

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

进行RGB图像转换是必要的,因为在PIL和IplImage中序列不同。Ipl到PIL也是如此。

但是如果使用opencv.adaptors,这已经得到了处理。如果感兴趣,可以查看adaptors.py中的详细信息。


3

我使用OpenCV2.1的python2.6绑定库实现了这个功能:

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

图像旋转和字符串翻转是将RGB交换为BGR,用于OpenCV视频编码。我认为这对于从PIL转换为OpenCV的任何其他图像使用也是必要的。

0

我不是专家,但我用这段代码成功地从PIL图像中获取了一个opencv图像:

import opencv

img = opencv.adaptors.PIL2Ipl(pilimg)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接