我有三个ndarray,分别是Y.shape(307200,)U.shape(153599,)V.shape(153599,)。使用opencv python将其转换为BGR的有效方法是什么?这些数组在YUV_420_888中 格式。
我的代码是
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)
答案 0 :(得分:1)
更新后的答案
信息here告诉我,一个Android NV21图像与所有Y(亮度)值连续存储,并以全分辨率采样,然后对V和U采样进行交织并以1/4的分辨率存储。分辨率(高度的1/2乘以宽度的1/2)。我在下面创建了一个虚拟的NV21框架,并将其转换为OpenCV BGR格式,这确认了布局以及OpenCV对其进行解释的方式。下面的所有代码均按从上到下的顺序工作,因此只需删除图像,然后将所有行排在一起就可以制作Python脚本:
#!/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)
给出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)
给出U:
# V is a white-black gradient from left to right
V = U[:,::-1]
# DEBUG
cv2.imwrite('V.jpg',V)
给出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)
这给哪个。希望您能看到单个Y,U和V分量的正确RGB表示。
因此,总而言之,我相信NV21图像中的2x2图像与交错VU一起存储,如下所示:
Y Y Y Y V U V U
,然后使用交错的UV存储2x2 NV12图像,如下所示:
Y Y Y Y U V U V
并将YUV420图像(Raspberry Pi)完全平面存储:
Y Y Y Y U U V V
原始答案
我没有可用于测试的数据,并且您的问题缺少一些详细信息,但是我发现5小时后没有人在回答您,因此我将尽力让您开始...没有人说答案必须完整。
首先,我想从您的Y.shape(307200)
看您的图像是640x480像素,对吗?
第二,您的U.shape(153599)
和V.shape(153599)
看起来不正确-它们应该是Y.shape
的一半,因为它们以2:1的速率采样。
一旦您解决了这个问题,我认为您需要获取Y
数组并附加U
数组,然后附加V
数组,以便只有一个连续的数组。然后,您需要使用代码cvtColor()
将其传递给cv2.CV_YUV2BGR_NV21
。
在附加之前,您可能需要重新调整数组的形状,例如im = Y.reshape(480,640)
。
我知道当您使用C ++接口连接OpenCV时,必须将图像的高度设置为实际高度的1.5倍(同时保持宽度不变)-因此您可能也需要尝试一下。
我永远记得OpenCV为图像打开模式(如IMREAD_ANYDEPTH
,IMREAD_GRAYSCALE
)和cvtColor()
提供的所有常量,因此这是一种方便的查找方法。我启动ipython
,如果要查找Android NV21常量,我会这样做:
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']
所以您需要的常量可能是COLOR_YUV2BGR_NV21
相同的技术适用于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']