Python OpenCV преобразует плоское изображение YUV 4: 2: 0 в RGB - формат массива YUV - PullRequest
3 голосов
/ 17 марта 2020

Я пытаюсь использовать OpenCV версии 4.1.0 до python для преобразования плоского изображения 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), и я должен поместить компоненты УФ в нижнюю половину 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, (1 образец Cr & Cb на 2x2 Y выборки)".

Вот Каналы yuv для образца изображения, отображаемого в оттенках серого:

Y Канал:

y channel

U Канал:

u channel

В Канал:

v channel

и соответствующее преобразование RGB (это было из использования выше Метод чередования, аналогичные артефакты видны при использовании метода «спина к спине»):

RGB-изображение с артефактами:

rgb image with artifacts

Как мне разместить информацию о каналах u и v в all_yuv_data?

Отредактировано Марком Сетчеллом после этой точки

Я считаю, что ожидаемый результат:

enter image description here

Ответы [ 2 ]

4 голосов
/ 18 марта 2020

В случае, если стандарт YUV соответствует формуле преобразования OpenCV COLOR_YUV2BGR_I420, вы можете прочитать кадр как один фрагмент и изменить его на высоту * 1,5 строки применяют преобразование.

Следующий пример кода:

  • Создает вход в формате YUV420 и записывает его в поток памяти (вместо fifo).
  • Считывание кадра из потока и преобразование его в BGR с использованием COLOR_YUV2BGR_I420.
    Цвета неверны ...
  • Повторите процесс, прочитав Y, U и V, изменив размеры U и V и с использованием преобразования COLOR_YCrCb2BGR.
    Примечание: OpenCV работает в цветном формате BGR (не RGB).

Вот код:

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()

Результат:
enter image description here

2 голосов
/ 18 марта 2020

Информация о каналах u и v, хранящаяся в нижней части yuv_array в этом вызове функции: cv2.cvtColor(yuv_array, cv2.COLOR_YUV420p2RGB)

должна быть отформатирована следующим образом:

  1. Верхний половина дополнительных строк, добавленных к нижней части массива yuv_array, заполнены информацией о вас. Ряды чередуются; первая строка u помещается непосредственно под информацией о канале y в левом слоте, а вторая строка u - в правом слоте в том же ряду yuv_data и т. д.
  2. канал v данные такие же, но для нижней половины дополнительных строк, добавленных в массив yuv_array.

Вот код конкатенации, который привел к ожидаемому изображению, опубликованному 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);

Вот изображение в градациях серого all_yuv_data в попытке ясности: grayscale image of all_yuv_data with y channel at the top, followed by the u and then the v channels

и результат после вызова cv2.cvtColor(all_yuv_data, cv2.COLOR_YUV420p2RGB): glorious image of the Grand Kingdom splash screen with correct coloring

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...