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

12 投票
4 回答
18322 浏览
提问于 2025-04-15 15:32

我想用OpenCV(在Python中)进行一些图像处理,但我得从一个PIL的Image对象开始,所以我不能使用cvLoadImage()这个函数,因为它需要一个文件名。

这个方法(改编自http://opencv.willowgarage.com/wiki/PythonInterface)不管用,因为cvSetData报错说argument 2 of type 'void *'。有没有什么好主意?

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的最后一个参数也不对,但我不太确定应该是什么。

4 个回答

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格式,可能也需要做这样的转换。

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中的具体细节。

9

你尝试适应的例子是针对OpenCV 2.0的新Python接口的。这可能是导致你对带前缀和不带前缀的函数名称感到困惑的原因(比如cv.cvSetData()cv.SetData())。

OpenCV 2.0现在提供了两种Python绑定方式:

  • 一种是“旧版”Python包装,这是一个包含opencv.{cv,highgui,ml}模块的Python包。
  • 另一种是新版接口,这是一个Python C扩展(cv.pyd),它封装了所有OpenCV的功能(包括highguiml模块)。

出现错误信息的原因是SWIG包装器无法将Python字符串转换为普通的C缓冲区。不过,SWIG包装器提供了opencv.adaptors模块,专门用于支持将numpyPIL图像转换为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缓冲区之间进行显式数据转换的实用函数。快速查看谷歌代码搜索似乎表明这是一种有效的方法。

关于cvSetData()函数的第三个参数,即图像缓冲区的大小,而不是图像步幅。步幅是图像每一行的字节数,计算公式是pixel_depth * number_of_channels * image_widthpixel_depth参数是与一个通道相关的数据大小(以字节为单位)。在你的例子中,它就是图像的宽度(只有一个通道,每个像素一个字节)。

撰写回答