【问题标题】:How do you marshal Python cv2.cv.LoadImage tostring data into C IplImage->imageData struct你如何将 Python cv2.cv.LoadImage 数据编组为 C IplImage->imageData 结构
【发布时间】:2014-04-30 09:07:26
【问题描述】:

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

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

例如,我有一个 2x2 像素的 4 像素图像。左上角像素为 100% 红色。右上角像素为 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 的数据组织方式完全不同,所以我查看调试器并惊讶地发现不仅数据不同,而且我无法理解:here it是,从 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 模型有问题。能解释一下吗?

【问题讨论】:

  • 嗯,过时的 cv2.cv api 将不会出现在即将到来的 opencv 版本中。也许您只是学习如何使用 cv2 / numpy ?
  • 我正在使用的库使用旧版本,我没有它的源代码。所以我现在必须让它以这种方式工作。无论如何,cv2 Mat 对象有同样的问题:“数据”属性类似于 IlpImage->imageData。
  • 我想我明白了!我稍后会发布一个完整的答案,但基本的缺失信息是 imageData 填充每一行,以便它可以被 4 整除。因此,在我上面的示例中,如果删除第 7 和第 8,然后删除第 15 和第 16 C 示例中的字节(这些是每一行的“结束”),您将拥有与 python 示例中相同的内容。

标签: python c++ c opencv iplimage


【解决方案1】:

我使用的 python 代码缺少一些所需的逻辑。此逻辑不适用于 Python 接口,并且在 Python 中不知道它在 C 库中是如何工作的。基本上,IplImage(我也相信 Mat;旧 IplImage 结构的 C++ 继任者)通过添加空(0 值)字节数来填充 imageData 属性中的像素行,使其可被 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 对象,这将是相同的。希望这对某人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-17
    • 1970-01-01
    • 2012-08-03
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多