如何将Python cv2.cv.LoadImage tostring数据编组到C IplImage-> imageData结构中

时间:2014-03-22 23:46:02

标签: python c++ c opencv iplimage

这个问题的根源是:什么是OpenCV IplImage-> imageData属性的逐位格式?

背景:我正在使用Python的ctypes来允许pythonic访问使用OpenCV的低级C库。我已经能够从python中获得几乎所有可以访问的函数,但是我仍然坚持这个需要旧的OpenCV结构的数据,称为IplImage,特别是imageData属性。我无法弄清楚IplImage-> imageData是如何组织的,而不是python的cv2.cv.LoadImage的iplimage类型,它与C结构表面上的数据相同,但看起来组织方式不同。

例如,我有一个2像素的4像素图像。左上角像素为100%RED。右上角像素为100%绿色。左下角像素为100%蓝色,右下角像素为100%白色。

在python中,信息如下所示:

import cv2

img = cv2.cv.LoadImage('rgbw.png')
pixels = []
for ch in img.tostring():
  pixels.append(ord(ch))
print pixels

[0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255]

这对我有意义:前三个值[0,0,255]代表B:0,G:0,R:255,红色像素。第二个是绿色,第三个是左下角,蓝色,最后一个右下角是白色。

我将它编组到库中,它的库表现得很好,但它似乎没有“看到”imageData中的任何内容(我得到一个返回代码,意思是“我什么都没看到”,当我明白这些数据是可理解的时候直接使用C api将其传递到库中。

所以当然我怀疑C IplImage-> imageData的数据组织方式完全不同,所以我查看调试器并惊讶地发现不仅数据不同,而且我无法理解:这里它是从cvLoadImage(“rgbw.png”)开始,将它分配给名为'image'的IplImage结构。

Breakpoint 1, main (argc=2, argv=0x7fffffffe418) at IplImageInfo.cpp:44
44          printf("imageData %s\n", image->imageData);
(gdb) x/16ub image->imageData
0x618c90:   0   0   255 0   255 0   0   0
0x618c98:   255 0   0   255 255 255 0   0
(gdb)

因此,逐字节比较,为了比较添加零:

的Python:

000 000 255 | 000 255 000 | 255 000 000 | 255 255 255

C :(打印前16个字节,而不是12个,这是我所期望的,见下文)

000 000 255 | 000 255 000 | 000 000 255 | 000 000 255 | 255 255 000 | 000

注意前两个字节在两者中是相同的。但那么,发生了什么?我们有另外两个红色像素,然后......一个青色像素?另外,这个文件大小为12个字节(4个像素,每个3个字节)。当我从C打印出image-> imageSize属性时,我得到16,而不是12.所以有些东西是烂的我不明白。显然我的imageData模型有问题。你能解释一下吗?

1 个答案:

答案 0 :(得分:1)

我使用的python代码缺少一些必需的逻辑。这个逻辑不适用于Python接口,并且Python中没有任何线索可以解释它在C库中是如何工作的。基本上,IplImage(我也相信Mat;旧的IplImage结构的C ++继承者)将imageData属性中的像素行填充为4,可以通过添加空(0值)字节数来对4进行整除。所以我的代码是这样的:

import cv2

img = cv2.cv.LoadImage('rgbw.png')
pixels = []
for ch in img.tostring():
  pixels.append(ord(ch))
print pixels

[0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255]

错过了这个逻辑。我解决了以下问题:

import cv2

img = cv2.cv.LoadImage('rgbw.png')
height = img.height
width = img.width
raw_data = img.tostring()

# iplImage->imageData requires rows to be padded with zero bytes at the end 
# so they be divisible by 4
pad_bytes_per_row = width % 4

# create the ctypes structure
ubyte_array_type = c_ubyte * (len(raw_data) + (height * pad_bytes_per_row))
ubyte_array = ubyte_array_type()
index = 0
for ch in raw_data:
    ubyte_array[index] = ord(ch)
    index += 1
    if 0 == index % width: # end of row
        pad_index = 0
        while pad_index < pad_bytes_per_row:
            ubyte_array[index] = 0
            pad_index += 1
            index += 1

现在ubyte_array填充了opencv的python API中的正确信息。请注意,如果您对数据使用numpy_array.tostring()方法并希望使用它来填充Mat对象,则这将是相同的。希望这有助于某人。