一、YUV格式
1、NV12、NV21格式
YUV420:NV12
YUV420:NV21
4个Y公用一个U、V,U、V是在Y之后存储的。Y的大小一般是width*height,U、V则在Y后面存储。
二、YUV转RGB公式
不同标准下RGB和YUV之间的转换公式不同,RGB的标准主要有BT601、BT709、BT2022三个标准。具体可以根据RGB标准进行进行转换,如果不了解应该转成什么标准,可以都试一下,看看哪种方式转换出的jpg和yuv更接近就用对应的转换公式。
1、BT601标准
1.1、8bit yuv
R、G、B ~ [0~255]
Y、U、V ~ [-128 ~ 128]
yuv -> rgb
R = Y + 1.4075 * (V - 128)
G = Y - 0.3455 * (U - 128) - 0.7169 * (V - 128)
B = Y + 1.779 * (U - 128)
rgb -> yuv
Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.169 * R - 0.331 * G + 0.5 * B + 128
V = 0.5 * R - 0.419 * G - 0.081 * B + 128
1.2、10bit yuv
R、G、B ~ [0~255]
Y、U、V ~ [-32768 ~ 32768]
yuv -> rgb
R = Y + 1.4075 * (V - 32768)
G = Y - 0.3455 * (U - 32768) - 0.7169 * (V - 32768)
B = Y + 1.779 * (U - 32768)
rgb -> yuv
Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.169 * R - 0.331 * G + 0.5 * B + 32768
V = 0.5 * R - 0.419 * G - 0.081 * B + 32768
2、BT709标准
2.1、8bit yuv
R、G、B ~ [0~255]
Y、U、V ~ [-128 ~ 128]
yuv -> rgb
R = Y + 1.5748 * (V - 128)
G = Y - 0.1868 * (U - 128) - 0.4680 * (V - 128)
B = Y + 1.856 * (U - 128)
rgb -> yuv
Y = 0.2126 * R + 0.7154 * G + 0.072 * B
U = -0.1145 * R - 0.3855 * G + 0.5 * B + 128
V = 0.5 * R - 0.4543 * G - 0.0457 * B + 128
2.2、10bit yuv
R、G、B ~ [0~255]
Y、U、V ~ [-32768 ~ 32768]
yuv -> rgb
R = Y + 1.5748 * (V - 32768)
G = Y - 0.1868 * (U - 32768) - 0.4680 * (V - 32768)
B = Y + 1.856 * (U - 32768)
rgb -> yuv
Y = 0.2126 * R + 0.7154 * G + 0.072 * B
U = -0.1145 * R - 0.3855 * G + 0.5 * B + 32768
V = 0.5 * R - 0.4543 * G - 0.0457 * B + 32768
3、BT2020标准
3.1、8bit yuv
R、G、B ~ [0~255]
Y、U、V ~ [-128 ~ 128]
yuv -> rgb
R = Y + 1.4746 * (V - 128)
G = Y - 0.1645 * (U - 128) - 0.5713 * (V - 128)
B = Y + 1.8814 * (U - 128)
rgb -> yuv
Y = 0.2627 * R + 0.6780 * G + 0.0593 * B
U = -0.1396 * R - 0.3604 * G + 0.5 * B + 128
V = 0.5 * R - 0.4598 * G - 0.0402 * B + 128
3.2、10bit yuv
R、G、B ~ [0~255]
Y、U、V ~ [-32768 ~ 32768]
yuv -> rgb
R = Y + 1.4746 * (V - 32768)
G = Y - 0.1645 * (U - 32768) - 0.5731 * (V - 32768)
B = Y + 1.8814 * (U - 32768)
rgb -> yuv
Y = 0.2627 * R + 0.6780 * G + 0.0593 * B
U = -0.1396 * R - 0.3604 * G + 0.5 * B + 32768
V = 0.5 * R - 0.4598 * G - 0.0402 * B + 32768
三、代码实现
使用python调用c++的方式进行实现,这种方式比单独使用python更快,转化效率更高。
1、python端
这里是以矩阵的方式求逆矩阵进行的转化,求逆矩阵使用python提供的方法更方便。
其实可以直接用上面的公式在C++端进行转化。但是通过打印逆矩阵的值,发现逆矩阵转出的结果再进行计算更精确一点点。
这里以BT601标准进行转化的,大家可以根据自己的需求换成不同标准对应的矩阵。
代码如下:
from PIL import Image
import numpy as np
'''
yuv_height yuv的高
yuv_width yuv的宽,这里传入stride
yuv_path yuv所在路径
yuv_type yuv的类型,这里指YUV420 NV12/NV21
BPP 指yuv的位深度,当前支持10bit、8bit
'''
def converyuv2jpg_8bpp(yuv_height, yuv_width, yuv_path, yuv_type, BPP):
print("opening file")
dest = os.path.splitext(yuv_path)[0] + '_' + yuv_type + '.jpg'
aa = np.array([[0.299, 0.587, 0.114], [-0.169, -0.331, 0.5], [0.5, -0.419, -0.081]])
aa_n = np.linalg.inv(aa)
print(aa_n[0][0])
print(aa_n)
c_data = (ctypes.c_double * 9)()
c_index = 0
for i in range(0, 3):
for j in range(0, 3):
c_data[c_index] = aa_n[i][j]
c_index += 1
flag_yuv_type = 0
print("read file")
lib = windll.LoadLibrary('./YUV2JPG.dll')
lib.yuv2rgb.restype = POINTER(c_uint8)
lib.yuv2rgb_ten.restype = POINTER(c_uint8)
if yuv_type == 'NV12':
flag_yuv_type = 0
else:
flag_yuv_type = 1
with open(yuv_path, 'rb') as f:
yuv = f.read()
print(len(yuv))
if BPP == '10':
rgb_bytes = bytearray(
lib.yuv2rgb_ten(c_data, yuv, len(yuv), flag_yuv_type, yuv_width,
yuv_height)[0:yuv_width * yuv_height * 3])
else:
rgb_bytes = bytearray(
lib.yuv2rgb(c_data, yuv, len(yuv), flag_yuv_type, yuv_width, yuv_height)
[0:yuv_width * yuv_height * 3])
print("finished conversion. Creating image object")
img = Image.frombytes("RGB", (yuv_width, yuv_height), bytes(rgb_bytes))
print("Image object created. Starting to save")
img.save(dest, "JPEG")
img.close()
2、C++端
这里需要编译成dll文件,将dll文件放到python同文件夹下。出问题可以使用cout或者printf进行打印,python运行窗口可以看见打印结果。
C++端10bit转换的时候,舍弃了低8位,这个没有想到更合理的方法,有懂的可以评论区进行讨论一下。
代码可拿去自行改进,加油!(ง •_•)ง
#include <iostream>
#include <stdio.h>
using namespace std;
extern "C" __declspec(dllexport) unsigned char* yuv2rgb(double* array, char* data, uint64_t data_length, int yuv_type, int width, int height);
extern "C" __declspec(dllexport) unsigned char* yuv2rgb_ten(double* array, uint16_t* data, uint64_t data_length, int yuv_type, int width, int height);
unsigned char* yuv2rgb(double* array, char* data, uint64_t data_length, int yuv_type, int width, int height)
{
for (int i = 0; i < 9; i++) {
printf("array[%d] = %f\n", i, array[i]);
}
printf("width = %d, height = %d\n", width, height);
uint64_t red_index = 0;
uint64_t green_index = 1;
uint64_t blue_index = 2;
uint64_t y_index = 0;
uint64_t u_index = 0;
uint64_t v_index = 0;
uint8_t Y = 0;
uint8_t U = 0;
uint8_t V = 0;
uint8_t R = 0;
uint8_t G = 0;
uint8_t B = 0;
bool flag = true;
static uint8_t rgb_bytes[9999 * 9999 * 3] = { 0 };
if (width > 0 && height > 0)
{
}
else
return nullptr;
// NV21
if (yuv_type == 1) {
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
v_index = width * height + (row / 2) * width + (column / 2) * 2;
u_index = u_index + 1;
if (y_index < data_length && v_index <= data_length && u_index <= data_length) {
Y = data[y_index];
U = data[u_index];
V = data[v_index];
if (array[0] * Y + array[1] * (U - 128) + array[2] * (V - 128) > 255) {
R = 255;
}
else if (array[0] * Y + array[1] * (U - 128) + array[2] * (V - 128) < 0) {
R = 0;
}
else {
R = array[0] * Y + array[1] * (U - 128) + array[2] * (V - 128);
}
if (array[3] * Y + array[4] * (U - 128) + array[5] * (V - 128) > 255) {
G = 255;
}
else if (array[3] * Y + array[4] * (U - 128) + array[5] * (V - 128) < 0) {
G = 0;
}
else {
G = array[3] * Y + array[4] * (U - 128) + array[5] * (V - 128);
}
if (array[6] * Y + array[7] * (U - 128) + array[8] * (V - 128) > 255) {
B = 255;
}
else if (array[6] * Y + array[7] * (U - 128) + array[8] * (V - 128) < 0) {
B = 0;
}
else {
B = array[6] * Y + array[7] * (U - 128) + array[8] * (V - 128);
}
//R = array[0] * Y + array[1] * U + array[2] * V;
//G = array[3] * Y + array[4] * U + array[5] * V;
//B = array[6] * Y + array[7] * U + array[8] * V;
rgb_bytes[red_index] = R;
rgb_bytes[green_index] = G;
rgb_bytes[blue_index] = B;
y_index += 1;
red_index += 3;
green_index += 3;
blue_index += 3;
}
}
}
}
// NV12
else {
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
u_index = width * height + (row / 2) * width + (column / 2) * 2;
v_index = u_index + 1;
if (y_index < data_length && v_index <= data_length && u_index <= data_length) {
Y = data[y_index];
U = data[u_index];
V = data[v_index];
if (array[0] * Y + array[1] * (U - 128) + array[2] * (V - 128) > 255) {
R = 255;
}
else if (array[0] * Y + array[1] * (U - 128) + array[2] * (V - 128) < 0) {
R = 0;
}
else {
R = array[0] * Y + array[1] * (U - 128) + array[2] * (V - 128);
}
if (array[3] * Y + array[4] * (U - 128) + array[5] * (V - 128) > 255) {
G = 255;
}
else if (array[3] * Y + array[4] * (U - 128) + array[5] * (V - 128) < 0) {
G = 0;
}
else {
G = array[3] * Y + array[4] * (U - 128) + array[5] * (V - 128);
}
if (array[6] * Y + array[7] * (U - 128) + array[8] * (V - 128) > 255) {
B = 255;
}
else if (array[6] * Y + array[7] * (U - 128) + array[8] * (V - 128) < 0) {
B = 0;
}
else {
B = array[6] * Y + array[7] * (U - 128) + array[8] * (V - 128);
}
rgb_bytes[red_index] = R;
rgb_bytes[green_index] = G;
rgb_bytes[blue_index] = B;
y_index += 1;
red_index += 3;
green_index += 3;
blue_index += 3;
}
}
}
}
return rgb_bytes;
}
unsigned char* yuv2rgb_ten(double* array, uint16_t* data, uint64_t data_length, int yuv_type, int width, int height)
{
data_length = data_length / 2;
printf("data len = %llu\n", data_length);
for (int i = 0; i < 9; i++) {
printf("array[%d] = %f\n", i, array[i]);
}
printf("width = %d, height = %d\n", width, height);
uint64_t red_index = 0;
uint64_t green_index = 1;
uint64_t blue_index = 2;
uint64_t y_index = 0;
uint64_t u_index = 0;
uint64_t v_index = 0;
uint16_t Y = 0;
uint16_t U = 0;
uint16_t V = 0;
uint16_t R = 0;
uint16_t G = 0;
uint16_t B = 0;
bool flag = true;
int iii = 0;
static uint8_t rgb_bytes[9999 * 9999 * 3 * 2] = { 0 };
if (width > 0 && height > 0)
{
}
else
return nullptr;
// NV21
if (yuv_type == 1) {
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
v_index = width * height + ((row / 2) * width + (column / 2) * 2) * 2;
u_index = u_index + 1;
if (y_index < data_length && v_index <= data_length && u_index <= data_length) {
Y = data[y_index];
U = data[u_index];
V = data[v_index];
if (array[0] * Y + array[1] * (U - 32767) + array[2] * (V - 32767) > 0xffff) {
R = 0xffff;
}
else if (array[0] * Y + array[1] * (U - 32767) + array[2] * (V - 32767) < 0) {
R = 0;
}
else {
R = array[0] * Y + array[1] * (U - 32767) + array[2] * (V - 32767);
}
if (array[3] * Y + array[4] * (U - 32767) + array[5] * (V - 32767) > 0xffff) {
G = 0xffff;
}
else if (array[3] * Y + array[4] * (U - 32767) + array[5] * (V - 32767) < 0) {
G = 0;
}
else {
G = array[3] * Y + array[4] * (U - 32767) + array[5] * (V - 32767);
}
if (array[6] * Y + array[7] * (U - 32767) + array[8] * (V - 32767) > 0xffff) {
B = 0xffff;
}
else if (array[6] * Y + array[7] * (U - 32767) + array[8] * (V - 32767) < 0) {
B = 0;
}
else {
B = array[6] * Y + array[7] * (U - 32767) + array[8] * (V - 32767);
}
rgb_bytes[red_index] = (R >> 8) & 0xff;
rgb_bytes[green_index] = (G >> 8) & 0xff;
rgb_bytes[blue_index] = (B >> 8) & 0xff;;
y_index += 1;
red_index += 3;
green_index += 3;
blue_index += 3;
}
}
}
}
// NV12
else {
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
u_index = width * height + (row / 2) * width + (column / 2) * 2;
v_index = u_index + 1;
if (y_index < data_length && v_index <= data_length && u_index <= data_length) {
Y = data[y_index];
U = data[u_index];
V = data[v_index];
if (array[0] * Y + array[1] * (U - 32767) + array[2] * (V - 32767) > 0xffff) {
R = 0xffff;
}
else if (array[0] * Y + array[1] * (U - 32767) + array[2] * (V - 32767) < 0) {
R = 0;
}
else {
R = array[0] * Y + array[1] * (U - 32767) + array[2] * (V - 32767);
}
if (array[3] * Y + array[4] * (U - 32767) + array[5] * (V - 32767) > 0xffff) {
G = 0xffff;
}
else if (array[3] * Y + array[4] * (U - 32767) + array[5] * (V - 32767) < 0) {
G = 0;
}
else {
G = array[3] * Y + array[4] * (U - 32767) + array[5] * (V - 32767);
}
if (array[6] * Y + array[7] * (U - 32767) + array[8] * (V - 32767) > 0xffff) {
B = 0xffff;
}
else if (array[6] * Y + array[7] * (U - 32767) + array[8] * (V - 32767) < 0) {
B = 0;
}
else {
B = array[6] * Y + array[7] * (U - 32767) + array[8] * (V - 32767);
}
rgb_bytes[red_index] = (R >> 8) & 0xff;
rgb_bytes[green_index] = (G >> 8) & 0xff;
rgb_bytes[blue_index] = (B >> 8) & 0xff;;
y_index += 1;
red_index += 3;
green_index += 3;
blue_index += 3;
}
}
}
}
return rgb_bytes;
}
int main()
{
return 0;
}