DRz DRz - 6 months ago 71
Python Question

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

Some context:

I have a packed BGRA image in a buffer that I would like to convert to RGB.

I use the following code to convert it to RGB using OpenCV:

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


But:

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


As OpenCV is open source, I have dived into the source code to figure out what happened.

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 );


And:

#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


I am not sure to understand these lines of code.

I assume
scn
is the "source channel number" and that it is related to the number of dimension of the array. The assertion would then fail because the array was created as a 1D array.

Indeed,
print np_a.ndim
outputs
1
and
print np_a.shape
outputs
(422400,)
.

I tried many things. Among them, setting the array's shape manually with
np_a.shape = (image_height, image_width)
, which ends with this error:

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


What am I missing?

Am I supposed to manually unpack the image before converting it? How?

EDIT:

The buffer is filled using a C API. It is supposed to be an array of UINT8.

Also, this:

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


Outputs:

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

Answer

If your buffer is 8-bit "packed", then all you're missing is a reshape:

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

It's not clear to me what BGRA2RGB does here - there's no "right" way to remove an alpha channel without choosing a background color. If the alpha data is garbage, you can go with the simpler

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

To ignore the alpha channel, and then flip the byte order. This is O(w*h) times faster than using opencv!

Note that if you plan to pass this array back to opencv, you might need to add:

rgb = np.copy(rgb)

Which makes the data contiguous in memory, a requirement of some opencv functions. This obviously loses you the efficiency gain mentioned above.