【问题标题】:How to convert YUV_420_888 to BGR using opencv python?如何使用 opencv python 将 YUV_420_888 转换为 BGR?
【发布时间】:2019-02-07 05:16:05
【问题描述】:

我有三个 ndarray,它们是 Y.shape(307200,) U.shape(153599,) V.shape(153599,)。使用opencv python将其转换为BGR的有效方法是什么?这些数组在YUV_420_888 格式。

my_image which is 640*640

我的代码是

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)

【问题讨论】:

    标签: python python-3.x numpy opencv


    【解决方案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)
    

    这给了你:

    # 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
    

    2x2 NV12 图像与交错 UV 一起存储,如下所示:

    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 数组,这样你就有一个连续的数组。然后您需要使用代码cv2.CV_YUV2BGR_NV21 将其传递给cvtColor()

    您可能需要在追加之前重塑您的数组,例如im = Y.reshape(480,640)

    我知道当你使用 OpenCV 的 C++ 接口时,你必须将图像的高度设置为实际高度的 1.5 倍(同时保持宽度不变)——所以你可能也需要尝试一下。

    我永远记不起 OpenCV 为图像打开模式(如IMREAD_ANYDEPTHIMREAD_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']
    

    【讨论】:

    • 抱歉,我说它未经测试且不完整 :-) 你想要的是COLOR_YUV2BGR_NV21 - 我拥有的是 C++ 版本。我已经更新了答案。
    • 错误消息意味着您传递给cvtColor() 的图像不是偶数像素宽,或者图像的高度不是 3 的倍数 - 这与 1.5x 高度有关我提到过,1.5 是 3/2。
    • 我的代码,img = np.reshape(YUV,(960,640)) img = cv2.cvtColor(img, cv2.COLOR_YUV2RGB_NV21),其中 960,640 是 1.5 倍,但我仍然遇到同样的错误 :(。有什么建议吗?:)
    • 请不要将代码放在 cmets 中,它很难阅读。相反,在您的原始问题下单击 edit 并在那里更新它 - 同时添加您的代码以创建 YUV 数组。我必须在一天的剩余时间里出去,但是当我有时间的时候会停下来。抱歉,我现在不能成为正常的、更有帮助的自我。
    • 现在我得到了我发布的有问题的图像,我希望是 (640*480) 但我得到 (640*640),有什么建议吗??
    猜你喜欢
    • 2022-01-26
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-17
    • 2013-06-13
    • 2018-11-30
    • 1970-01-01
    相关资源
    最近更新 更多