Relationship between YUV and RGB
Relationship between YUV and RGB
-
-
1. YUV格式
-
- 1.1 基本概念
- 1.2 YUV420
-
- 1.2.1 YUV420P
-
1.2.2 YUV420SP
-
2. YUV420sp to RGB
-
- 2.1 YUV420sp 8bits
- 2.2 YUV420sp 10bits
-
3. RGB to YUV444
-
- 3.1 RGB to YUV 444
- 3.2 RGB to YUV420
-
此信息也可在维基百科中查阅:如维基百科所述:https://zh.wikipedia.org/wiki/YUV>
色彩深度: 如维基百科所述:https://en.wikipedia.org/wiki/Color_depth>
1. YUV格式
1.1 基本概念
YUV的颜色空间中所使用的像素位深分别为8-bit、10-bit、12-bit及16-bit。
每个像素占用的内存量会有所差异。
对于8-bit的值来说,在编程实现时可以直接使用int8类型进行表示。
而针对10位以上的情况,则需要根据具体应用场景来决定数值的具体读取方式:
一般而言,在处理不同比特深度时,
优先采用int16类型来进行数值运算;
如果遇到无法完全填充的情况,
则会将多余的高位部分赋值为零。
在多数情况下,默认顺序是先存储Y数据块,
随后按照不同的压缩格式分别处理U和V通道的数据。
这些压缩格式主要包括以下几种:
YCbCr_4:2:0、YCbCr_4:2:2、
YCbCr_4:1:1以及标准全分辨率格式下的
YCbCr_4:4:4编码方案。
-
4:4:4表示完全取样。
占用的空间内存为:
size = height * width * 3 -
4:2:2表示2:1的水平采样,垂直完全采样。
占用的空间内存为:
size = height * width * (1+1) -
4:2:0表示2:1的水平采样,垂直2:1采样。
占用的空间内存为:
size = height * width * (1+ 1/2) -
4:1:1表示4:1的水平采样,垂直完全采样。
占用的空间内存为:
size = height * width * (1+ 1/2)
1.2 YUV420
通常YUV420的存储格式为:

通常情况下,在数据处理流程中我们会按照一定的顺序执行各项操作;在YUV格式中遵循特定规则对图像进行编码时会遇到不同的处理方式:首先我们会对图像进行Y通道提取随后处理U通道最后处理V通道以完成整个编码过程;对于常见的YUV420格式其基本形式包括YUV420P(平面形式)和YUV420SP(半平面形式)。
1.2.1 YUV420P
对于YUV420P格式及其相关编码方式中包含I420和YV12两种编码格式:其中YU12(I420): YYYYYYYY UUVV即顺序为先读取Y分量再依次读取U分量和V分量

YV12: YYYYYYYY VVUU
先读取Y后读取V最后读取V

1.2.2 YUV420SP
遵循YUV420SP标准的图像阵列遵循先读取亮度分量(Y)后依次读取色差分量(U或V)的方式存储按照U-V或V-U顺序依次存储
NV12:先存储Y后,UV交替存储,即YYYYUVUV

NV21:先存储Y后,VU交替存储,即 YYYYVUVU

2. YUV420sp to RGB
# -*- coding: utf-8 -*-
from PIL import Image
import time, shutil, sys
from time import perf_counter
import numpy as np
from matplotlib import pyplot as plt
from skimage import io
path = '10_0.yuv'
def readyuv(path, r, c, bits):
with open(path,'rb') as fp:
if bits == 8:
data_bytes = fp.read(r*c+int(r*c/2))
data_bytes = np.fromstring(data_bytes,'int8')
if bits == 10:
data_bytes = fp.read(2*(r*c+int(r*c/2)))
data_bytes = np.fromstring(data_bytes,'int16')
my = data_bytes[0 : r*c]
mu = data_bytes[r*c:r*c+int(r*c/2):2]
mv = data_bytes[r*c+1: r*c+int(r*c/2):2]
imy = np.reshape(my, (c, r))
imu = np.reshape(mu, (int(c/2), int(r/2)))
imv = np.reshape(mv, (int(c/2), int(r/2)))
if bits == 10:
imy1 = imy1 / 1023
imy = imy1.astype(int)
imu1 = imu1 / 1023
imu = imu1.astype(int)
imv1 = imv1 / 1023
imv = imv1.astype(int)
return imy, imu, imv
data = read_yuv1(path2, 4000, 3000, bits=10)
plt.figure()
plt.imshow(data)
data1 = data.astype(int)
plt.figure()
plt.imshow(data1)
将YUV转化为RGB,范围是[0, 255],需要进行计算
计算公式如下所示:
\begin{bmatrix} {R}\\ {G}\\ {B}\\ \end{bmatrix} = \begin{bmatrix} {298}&{0}&{459}\\ {298}&{-55}&{-137}\\ {298}&{541}&{0}\\ \end{bmatrix} * \begin{bmatrix} {Y-16}\\ {U-128}\\ {V-128}\\ \end{bmatrix} + \begin{bmatrix} {128}\\ {128}\\ {128}\\ \end{bmatrix} >> \begin{bmatrix} {8}\\ {8}\\ {8}\\ \end{bmatrix}
公式的准确度待考证, 目前没有见到更加官方的公式来计算。很多公式感觉比较乱,而且系数的差异不大。计算的时候需要对每个像素点的范围进行约束,范围需要到[0, 255],如果超过可这个范围,整个图像会发雾。
def read_yuv1(path, r, c, bits):
with open(path,'rb') as fp:
imy1, imu1, imv1 = readyuv(path, r, c, bits)
imy1 = imy1 / 1023
imy = imy1.astype(int)
imu1 = imu1 / 1023
imu = imu1.astype(int)
imv1 = imv1 / 1023
imv = imv1.astype(int)
rgb = np.zeros((c, r, 3))
for i in range(0, c, 2):
for j in range(0, r, 2):
m = int(i/2)
n = int(j/2)
rgb[i][j][0] = (298*(imy[i][j]-16) + 459*(imv[m][n]-128)+128) >>8
if rgb[i][j][0] < 0:
rgb[i][j][0] = 0
if rgb[i][j][0] > 255:
rgb[i][j][0] = 255
rgb[i][j][1] = (298*(imy[i][j]-16) - 55*(imu[m][n]-128) - 137 *(imv[m][n]-128) +128) >>8
if rgb[i][j][1] < 0:
rgb[i][j][1] = 0
if rgb[i][j][1] > 255:
rgb[i][j][1] = 255
rgb[i][j][2] = (298*(imy[i][j]-16) + 541*(imu[m][n]-128) +128) >>8
if rgb[i][j][2] < 0:
rgb[i][j][2] = 0
if rgb[i][j][2] > 255:
rgb[i][j][2] = 255
#[i, j+1]
rgb[i][j+1][0] = (298*(imy[i][j+1]-16) + 459*(imv[m][n]-128)+128) >>8
if rgb[i][j+1][0] < 0:
rgb[i][j+1][0] = 0
if rgb[i][j+1][0] > 255:
rgb[i][j+1][0] = 255
rgb[i][j+1][1] = (298*(imy[i][j+1]-16) - 55*(imu[m][n]-128) - 137 *(imv[m][n]-128) +128) >>8
if rgb[i][j+1][1] < 0:
rgb[i][j+1][1] = 0
if rgb[i][j+1][1] > 255:
rgb[i][j+1][1] = 255
rgb[i][j+1][2] = (298*(imy[i][j+1]-16) + 541*(imu[m][n]-128) +128) >>8
if rgb[i][j+1][2] < 0:
rgb[i][j+1][2] = 0
if rgb[i][j+1][2] > 255:
rgb[i][j+1][2] = 255
#[i+1, j]
rgb[i+1][j][0] = (298*(imy[i+1][j]-16) + 459*(imv[m][n]-128)+128) >>8
if rgb[i+1][j][0] < 0:
rgb[i+1][j][0] = 0
if rgb[i+1][j][0] > 255:
rgb[i+1][j][0] = 255
rgb[i+1][j][1] = (298*(imy[i+1][j]-16) - 55*(imu[m][n]-128) - 137 *(imv[m][n]-128) +128) >>8
if rgb[i+1][j][1] < 0:
rgb[i+1][j][1] = 0
if rgb[i+1][j][1] > 255:
rgb[i+1][j][1] = 255
rgb[i+1][j][2] = (298*(imy[i+1][j]-16) + 541*(imu[m][n]-128) +128) >>8
if rgb[i+1][j][2] < 0:
rgb[i+1][j][2] = 0
if rgb[i+1][j][2] > 255:
rgb[i+1][j][2] = 255
#[i+1, j+1]
rgb[i+1][j+1][0] = (298*(imy[i+1][j+1]-16) + 459*(imv[m][n]-128)+128) >>8
if rgb[i+1][j+1][0] < 0:
rgb[i+1][j+1][0] = 0
if rgb[i+1][j+1][0] > 255:
rgb[i+1][j+1][0] = 255
rgb[i+1][j+1][1] = (298*(imy[i+1][j+1]-16) - 55*(imu[m][n]-128) - 137 *(imv[m][n]-128) +128) >>8
if rgb[i+1][j+1][1] < 0:
rgb[i+1][j+1][1] = 0
if rgb[i+1][j+1][1] > 255:
rgb[i+1][j+1][1] = 255
rgb[i+1][j+1][2] = (298*(imy[i+1][j+1]-16) + 541*(imu[m][n]-128) +128) >>8
if rgb[i+1][j+1][2] < 0:
rgb[i+1][j+1][2] = 0
if rgb[i+1][j+1][2] > 255:
rgb[i+1][j+1][2] = 255
return rgb
2.1 YUV420sp 8bits
RGB:

Y:

U:

V:

2.2 YUV420sp 10bits
RGB:

Y:

U:

V:

3. RGB to YUV444
RGB转YUV的公式如下所示:
3.1 RGB to YUV 444
\begin{bmatrix} {Y}\\ {U}\\ {V}\\ \end{bmatrix} = \begin{bmatrix} {0.299}&{0.587}&{0.114}\\ {-0.147}&{-0.289}&{0.436}\\ {0.615}&{-0.515}&{-0.1}\\ \end{bmatrix} * \begin{bmatrix} {R}\\ {G}\\ {B}\\ \end{bmatrix}
相应的代码为:
def rgb2yuv(path, r, c):
I = io.imread(path1)
Y = (0.299*I[:,:,0] +0.587*I[:,:,1] + 0.114*I[:,:,2])
V = (0.615*I[:,:,0] -0.515*I[:,:,1] - 0.1*I[:,:,2])
U = (-0.147*I[:,:,0] -0.289*I[:,:,1] + 0.436*I[:,:,2])
return Y, U, V
Y:

V:

U:

3.2 RGB to YUV420
RGB to YUV420, 直接取UV的四个值里的第一个值作为U和V
代码
def rgb2yuv(path, r, c):
I = io.imread(path1)
Y = (0.299*I[:,:,0] +0.587*I[:,:,1] + 0.114*I[:,:,2])
V = (0.615*I[:,:,0] -0.515*I[:,:,1] - 0.1*I[:,:,2])
U = (-0.147*I[:,:,0] -0.289*I[:,:,1] + 0.436*I[:,:,2])
u = np.zeros((int(c/2), int(r/2)))
v = np.zeros((int(c/2), int(r/2)))
for i in range(0, int(c/2)):
for j in range(0, int(r/2)):
u[i, j] = U[i*2, j*2]
v[i, j] = V[i*2, j*2]
return Y, U, V, u, v
