在 Python 中使用 OpenCV 将打包的 BGRA 图像缓冲区转换为 RGB 时遇到问题

Trouble converting packed BGRA image buffer to RGB with OpenCV in Python

本文关键字:转换 缓冲区 图像 RGB 问题 遇到 BGRA Python OpenCV      更新时间:2023-10-16

一些上下文:
我在缓冲区中有一个打包的 BGRA 图像,我想将其转换为 RGB。

我使用以下代码通过OpenCV将其转换为RGB:

np_a = np.array( image_buffer ) #image_buffer is an array of uint8
rgb_a = cv2.cvtColor( image_buffer, cv2.COLOR_BGRA2RGB )

但:

OpenCV Error: Assertion failed (scn == 3 || scn == 4) in ipp_cvtColor,
file /home/username/opencv/opencv-3.1.0/modules/imgpro/src/color.cpp, line 7341

由于OpenCV是开源的,我已经深入研究了源代码以弄清楚发生了什么。

static bool ipp_cvtColor( Mat &src, OutputArray _dst, int code, int dcn )
{
int stype = src.type();
int scn = CV_MAT_CN(stype), depth = CV_MAT_DEPTH(stype);
Mat dst;
Size sz = src.size();
switch( code )
{
#if IPP_VERSION_X100 >= 700
case CV_BGR2BGRA: case CV_RGB2BGRA: case CV_BGRA2BGR:
case CV_RGBA2BGR: case CV_RGB2BGR: case CV_BGRA2RGBA:
CV_Assert( scn == 3 || scn == 4 );

和:

#define CV_MAT_CN (flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3

我不确定是否理解这些代码行。
我假设scn是"源通道号",并且它与数组的维数有关。然后断言将失败,因为数组是作为一维数组创建的。
事实上,print np_a.ndim输出1print np_a.shape输出(422400,)

我尝试了很多东西。其中,使用np_a.shape = (image_height, image_width)手动设置数组的形状,以此错误结尾:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000570558 in visit_decref ()

我错过了什么?
我应该在转换之前手动解压缩图像吗?如何?


第一次编辑:
缓冲区使用 C API 填充。它应该是UINT8的数组。

另外,这个:

print type( np_a )
print type( np_a[ 0 ] )
print np_a.shape

输出:

<type 'numpy.ndarray'>
<type 'numpy.uint8'>
(422400,)

第二次编辑:

问题已经解决了,这只是为了更好地理解/另一种方式。

用:

np_a           = np.array( image_buffer )
np_a_reshaped  = np_a.reshape( height, width, depth )
np_a_converted = np_a_reshaped[ ...,:3 ][ ...,::-1 ]
print len( np_a_converted )

输出:480。

所以,是的,我可能单独使用np_a.reshape( ... ),并假设它会改变np_a的形状。为什么要更改缓冲区的形状创建新变量?

但是,np_a_converted的大小仍然不正确。实际上,在程序的后面,有以下代码:

img = wx.ImageFromBuffer( width, height, np_a_converted )
bmp = wx.Bitmap( img )

要创建 wx。缓冲区中的位图,不复制数据。

从wx。ImageFromBuffer的文档:

dataBuffer 对象应包含一系列 RGB 字节和 宽度*高度*3字节长。

它给出了这个错误:

File "/usr/local/lib/python2.7/dist-packages/wx/core.py", line 656, in ImageFromBuffer
img.SetDataBuffer(dataBuffer)
ValueError: Invalid data buffer size.

如果您的缓冲区是 8 位"打包"的,那么您缺少的只是一个reshape

image = image_buffer.reshape(height, width, 4)
rgb = cv2.cvtColor(image, cv2.COLOR_BGRA2RGB)

我不清楚BGRA2RGB在这里做什么 - 没有"正确"的方法可以在不选择背景颜色的情况下删除 alpha 通道。如果alpha数据是垃圾,你可以选择更简单的

rgb = image[...,:3][...,::-1]

忽略 alpha 通道,然后翻转字节顺序。这比使用 opencv 快O(w*h)倍!

请注意,如果您打算将此数组传递回 opencv,则可能需要添加:

rgb = np.copy(rgb)

这使得数据在内存中是连续的,这是一些opencv函数的要求。这显然会让您失去上述效率提升。

正如您所说,您也在使用 C API,以下是您在 C++ 中执行此操作的方法。

假设您将 8 位精度的 BGRA 数据存储在uchar* buffer中。

然后你所要做的就是将此缓冲区转换为如下所示的Vec4b*

Vec4b* new_buffer = (Vec4b*) buffer;

然后像这样创建映像:

cv::Mat image(height, width, CV_8UC4, new_buffer);

然后,您可以应用cvtColor函数。

cv::cvtColor(image, destination, CV_BGRA2BGR) ;

编辑:

实际上,你甚至不需要演员阵容。可以直接将数据传递给构造函数:

cv::Mat image(height, width, CV_8UC4, buffer);

我找到了解决问题的方法:

  • 分段错误是由于缓冲区大小的公式错误造成的。

  • 然后,我使用np_a.shape = (image_height, image_width, image_depth)将缓冲区结构设置为 4 通道图像(断言失败,因为缓冲区被读取为 1 维数组)。

    事实上,现在,print np_a.shape输出(480, 640, 4).

    不知何故,埃里克提出的np_a.reshape( image_height, image_width, image_depth )不起作用。