[英]How to extract frames from a .yuv video file (YUV420) using python and openCV?
[英]How to convert YUV_420_888 to BGR using opencv python?
我有三個 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)
更新答案
這里的信息告訴我,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_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']
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.