这个问题的根源是:什么是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模型有问题。你能解释一下吗?
答案 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对象,则这将是相同的。希望这有助于某人。