![](/img/trans.png)
[英]Convert RGB image to YUV and YCbCr color space image in Opencv Python
[英]Python OpenCV converting planar YUV 4:2:0 image to RGB -- YUV array format
我正在嘗試通過 python 使用 OpenCV 4.1.0 版將平面 YUV 4:2:0 圖像轉換為 RGB,並且正在努力理解如何格式化數組以傳遞給cvtColor
函數。 我將所有 3 個通道作為單獨的數組,並嘗試將它們合並以與cv2.cvtColor
一起使用。 我正在使用cv2.cvtColor(yuv_array, cv2.COLOR_YUV420p2RGB)
。 據我所知, yuv_array
應該是1.5倍一樣高的原始圖像(這是從YUV陣列cvtColor
使用cv2.COLOR_RGB2YUV_YV12
的樣子),我應該把UV成分進入的下半部yuv_array
和Y通道進入數組的頂部。
我似乎無法弄清楚 U 和 V 通道應該如何在這個陣列的底部格式化。 我試過將它們交錯,然后將它們背靠背放在那里。 使用這兩種方法,我都嘗試先將 U 放在 V 中,反之亦然。 所有方法都會導致生成的圖像中出現偽影。 這是我的代碼和示例圖像:
import os
import errno
import numpy as np
import cv2
fifo_names = ["/tmp/fifos/y_fifo", "/tmp/fifos/u_fifo", "/tmp/fifos/v_fifo"]
#teardown; delete fifos
import signal, sys
def cleanup_exit(signal, frame):
print ("cleaning up!")
for fifo in fifo_names:
os.remove(fifo)
sys.exit(0)
signal.signal(signal.SIGINT, cleanup_exit)
signal.signal(signal.SIGTERM, cleanup_exit)
#make fifos
for fifo in fifo_names:
try:
os.mkfifo(fifo);
except OSError as oe:
if oe.errno == errno.EEXIST:
os.remove(fifo)
os.mkfifo(fifo)
else:
raise()
#make individual np arrays to store Y,U, and V channels
#we know the image size beforehand -- 640x360 pixels
yuv_data = []
frame_size = []
fullsize = (360, 640)
halfsize = (180, 320)
for i in range(len(fifo_names)):
if (i == 0):
size = fullsize
else:
size = halfsize
yuv_data.append(np.empty(size, dtype=np.uint8));
frame_size.append(size)
#make array that holds all yuv data for display with cv2
all_yuv_data = np.empty((fullsize[0] + halfsize[0], fullsize[1]), dtype=np.uint8)
#continuously read yuv images from fifos
print("waiting for fifo to be written to...")
while True:
for i in range(len(fifo_names)):
fifo = fifo_names[i]
with open(fifo, 'rb') as f:
print("FIFO %s opened" % (fifo))
all_data = b''
while True:
data = f.read()
print("read from %s, len: %d" % (fifo,len(data)))
if len(data) == 0: #then the fifo has been closed
break
else:
all_data += data
yuv_data[i] = np.frombuffer(all_data, dtype=np.uint8).reshape(frame_size[i])
#stick all yuv data in one buffer, interleaving columns
all_yuv_data[0:fullsize[0],0:fullsize[1]] = yuv_data[0]
all_yuv_data[fullsize[0]:,0:fullsize[1]:2] = yuv_data[1]
all_yuv_data[fullsize[0]:,1:fullsize[1]:2] = yuv_data[2]
#show each yuv channel individually
cv2.imshow('y', yuv_data[0])
cv2.imshow('u', yuv_data[1])
cv2.imshow('v', yuv_data[2])
#convert yuv to rgb and display it
rgb = cv2.cvtColor(all_yuv_data, cv2.COLOR_YUV420p2RGB);
cv2.imshow('rgb', rgb)
cv2.waitKey(1)
上面的代碼試圖按列交錯 U 和 V 信息。
我還嘗試使用以下方法將 U 和 V 通道信息放入all_yuv_data
數組:
#try back-to-back
all_yuv_data[0:fullsize[0],0:fullsize[1]] = yuv_data[0]
all_yuv_data[fullsize[0]:,0:halfsize[1]] = yuv_data[1]
all_yuv_data[fullsize[0]:,halfsize[1]:] = yuv_data[2]
圖像是用libav從另一個程序中獲取的一幀視頻。 該幀的格式為AV_PIX_FMT_YUV420P
, 描述為“平面 YUV 4:2:0, 12bpp,(每 2x2 Y 樣本 1 Cr 和 Cb 樣本)”。
以下是以灰度顯示的示例圖像的 yuv 通道:
Y頻道:
U頻道:
視頻頻道:
以及相應的 RGB 轉換(這是使用上述交錯方法,使用“背對背”方法時會看到類似的偽影):
帶有偽像的 RGB 圖像:
我應該如何在all_yuv_data
放置 u 和 v 通道信息?
在此之后由 Mark Setchell 編輯
我相信預期的結果是:
如果 YUV 標准匹配 OpenCV COLOR_YUV2BGR_I420
轉換公式,您可以將幀讀取為一個塊,並將其重塑為高度*1.5 行應用轉換。
以下代碼示例:
COLOR_YUV2BGR_I420
將其轉換為 BGR。COLOR_YCrCb2BGR
轉換來重復該過程。這是代碼:
import cv2
import numpy as np
import io
# Building the input:
###############################################################################
img = cv2.imread('GrandKingdom.jpg')
#yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
#y, u, v = cv2.split(yuv)
# Convert BGR to YCrCb (YCrCb apply YCrCb JPEG (or YCC), "full range",
# where Y range is [0, 255], and U, V range is [0, 255] (this is the default JPEG format color space format).
yvu = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
y, v, u = cv2.split(yvu)
# Downsample U and V (apply 420 format).
u = cv2.resize(u, (u.shape[1]//2, u.shape[0]//2))
v = cv2.resize(v, (v.shape[1]//2, v.shape[0]//2))
# Open In-memory bytes streams (instead of using fifo)
f = io.BytesIO()
# Write Y, U and V to the "streams".
f.write(y.tobytes())
f.write(u.tobytes())
f.write(v.tobytes())
f.seek(0)
###############################################################################
# Read YUV420 (I420 planar format) and convert to BGR
###############################################################################
data = f.read(y.size*3//2) # Read one frame (number of bytes is width*height*1.5).
# Reshape data to numpy array with height*1.5 rows
yuv_data = np.frombuffer(data, np.uint8).reshape(y.shape[0]*3//2, y.shape[1])
# Convert YUV to BGR
bgr = cv2.cvtColor(yuv_data, cv2.COLOR_YUV2BGR_I420);
# How to How should I be placing the u and v channel information in all_yuv_data?
# -------------------------------------------------------------------------------
# Example: place the channels one after the other (for a single frame)
f.seek(0)
y0 = f.read(y.size)
u0 = f.read(y.size//4)
v0 = f.read(y.size//4)
yuv_data = y0 + u0 + v0
yuv_data = np.frombuffer(yuv_data, np.uint8).reshape(y.shape[0]*3//2, y.shape[1])
bgr = cv2.cvtColor(yuv_data, cv2.COLOR_YUV2BGR_I420);
###############################################################################
# Display result:
cv2.imshow("bgr incorrect colors", bgr)
###############################################################################
f.seek(0)
y = np.frombuffer(f.read(y.size), dtype=np.uint8).reshape((y.shape[0], y.shape[1])) # Read Y color channel and reshape to height x width numpy array
u = np.frombuffer(f.read(y.size//4), dtype=np.uint8).reshape((y.shape[0]//2, y.shape[1]//2)) # Read U color channel and reshape to height x width numpy array
v = np.frombuffer(f.read(y.size//4), dtype=np.uint8).reshape((y.shape[0]//2, y.shape[1]//2)) # Read V color channel and reshape to height x width numpy array
# Resize u and v color channels to be the same size as y
u = cv2.resize(u, (y.shape[1], y.shape[0]))
v = cv2.resize(v, (y.shape[1], y.shape[0]))
yvu = cv2.merge((y, v, u)) # Stack planes to 3D matrix (use Y,V,U ordering)
bgr = cv2.cvtColor(yvu, cv2.COLOR_YCrCb2BGR)
###############################################################################
# Display result:
cv2.imshow("bgr", bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()
本次函數調用中yuv_array
底部存儲的u和v通道信息: cv2.cvtColor(yuv_array, cv2.COLOR_YUV420p2RGB)
預計格式如下:
yuv_data
的同一行中的右側槽中,依此類推。以下是在放置在原始程序中時產生 MarkSetchnell 發布的預期圖像的連接代碼:
#place y channel into buffer
all_yuv_data[0:fullsize[0],0:fullsize[1]] = yuv_data[0]
#formatted as interleaved u rows on top, (half on left, half on right)
#and interleaved v rows on bottom
all_yuv_data[fullsize[0]:fullsize[0]+halfsize[0]//2, :] = yuv_data[1].reshape(-1, fullsize[1])
all_yuv_data[fullsize[0]+halfsize[0]//2:,:] = yuv_data[2].reshape(-1, fullsize[1])
#convert to rgb
rgb = cv2.cvtColor(all_yuv_data, cv2.COLOR_YUV420p2RGB);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.