简体   繁体   中英

How to convert YUV_420_888 to BGR using opencv python?

I have three ndarray which is Y.shape(307200,) U.shape(153599,) V.shape(153599,). what is the efficient way to convert this to BGR using opencv python? Those array are in YUV_420_888 format.

my_image which is 640*640

My code is

Y= np.fromstring(Y, dtype=np.uint8)
U= np.fromstring(U, dtype=np.uint8)
V= np.fromstring(V, dtype=np.uint8)
Y= np.reshape(Y, (480,640))
U= np.reshape(U, (480,320))
V= np.reshape(V, (480,320))
YUV = np.append(Y,U)
YUV = np.append(YUV,V)
img = np.reshape(YUV,(960,640))
img = np.asarray(img, dtype = np.uint8)
img = cv2.cvtColor(img, cv2.COLOR_YUV2BGR_NV21)

Updated Answer

The information here tells me that an Android NV21 image is stored with all the Y (Luminance) values contiguously and sampled at the full resolution followed by the V and the U samples interleaved and stored at 1/4 the resolution (1/2 the height by 1/2 the width). I have created a dummy NV21 frame below and converted it into OpenCV BGR format and that confirms the layout and the way OpenCV interprets it too. All the code below works in order from top to bottom, so just remove the images and squidge all the lines up together to make a Python script:

#!/usr/bin/env python3

import cv2
import numpy as np

# Define width and height of image
w,h = 640,480

# Create black-white gradient from top to bottom in Y channel
f = lambda i, j: int((i*256)/h)
Y = np.fromfunction(np.vectorize(f), (h,w)).astype(np.uint8) 
# DEBUG
cv2.imwrite('Y.jpg',Y)

That gives Y:

在此处输入图片说明

# Dimensions of subsampled U and V
UVwidth, UVheight = w//2, h//2

# U is a black-white gradient from left to right
f = lambda i, j: int((j*256)/UVwidth)
U = np.fromfunction(np.vectorize(f), (UVheight,UVwidth)).astype(np.uint8)  
# DEBUG 
cv2.imwrite('U.jpg',U)

That gives U:

在此处输入图片说明

# V is a white-black gradient from left to right
V = U[:,::-1] 
# DEBUG 
cv2.imwrite('V.jpg',V)

That gives V:

在此处输入图片说明

# Interleave U and V, V first NV21, U first for NV12
U = np.ravel(U)
V = np.ravel(V)
UV = np.empty((U.size+V.size), dtype=np.uint8)
UV[0::2] = V
UV[1::2] = U

# Lay out Y plane, followed by UV
YUV = np.append(Y,UV).reshape((3*h)//2,w)

BGR = cv2.cvtColor(YUV.astype(np.uint8), cv2.COLOR_YUV2BGR_NV21)
cv2.imwrite('result.jpg',BGR)

Which gives this. Hopefully you can see how that is the correct RGB representation of the individual Y, U, and V components.

在此处输入图片说明

So, in summary, I believe a 2x2 image in NV21 image is stored with interleaved VU, like this:

Y Y Y Y V U V U

and a 2x2 NV12 image is stored with interleaved UV, like this:

Y Y Y Y U V U V

and a YUV420 image (Raspberry Pi) is stored fully planar:

Y Y Y Y U U V V

Original Answer

I don't have your data to test with and your question is missing some details, but I see no-one is answering you after 5 hours, so I'll try and get you started... no-one said answers have to be complete.

Firstly, I guess from your Y.shape(307200) that your image is 640x480 pixels, correct?

Secondly, your U.shape(153599) and V.shape(153599) look incorrect - they should be exactly half the Y.shape since they are sampled down at a rate of 2:1.

Once you have got that sorted out, I think you need to take your Y array and append the U array, then the V array so you have one single contiguous array. You then need to pass that to cvtColor() with the code cv2.CV_YUV2BGR_NV21 .

You may need to reshape your array before appending, something like im = Y.reshape(480,640) .

I know when you use the C++ interface to OpenCV, you must set the height of the image to 1.5x the actual height (whilst leaving the width unchanged) - so you may need to try that too.

I can never remember all the constants OpenCV provides for image opening modes (like IMREAD_ANYDEPTH , IMREAD_GRAYSCALE ) and for cvtColor() , so here's a handy way of finding them. I start ipython and if am looking for the Android NV21 constants, I do:

import cv2

[i for i in dir(cv2) if 'NV21' in i]                                                                                                                       
Out[29]: 
['COLOR_YUV2BGRA_NV21',
 'COLOR_YUV2BGR_NV21',
 'COLOR_YUV2GRAY_NV21',
 'COLOR_YUV2RGBA_NV21',
 'COLOR_YUV2RGB_NV21']

So the constant you need is probably COLOR_YUV2BGR_NV21

The same technique works for parameters to imread() :

items=[i for i in dir(cv2) if i.startswith('IMREAD')]                                                                                                                                                          

In [22]: items                                                                                                                                                      

['IMREAD_ANYCOLOR',
 'IMREAD_ANYDEPTH',
 'IMREAD_COLOR',
 'IMREAD_GRAYSCALE',
 'IMREAD_IGNORE_ORIENTATION',
 'IMREAD_LOAD_GDAL',
 'IMREAD_REDUCED_COLOR_2',
 'IMREAD_REDUCED_COLOR_4',
 'IMREAD_REDUCED_COLOR_8',
 'IMREAD_REDUCED_GRAYSCALE_2',
 'IMREAD_REDUCED_GRAYSCALE_4',
 'IMREAD_REDUCED_GRAYSCALE_8',
 'IMREAD_UNCHANGED']

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM