yuv转jpg(NV12,NV21)

一、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;
}

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Para convertir una imagen en formato cv::Mat a yuv_nv12, se puede utilizar la siguiente función: ``` void mat2yuv_nv12(const cv::Mat& mat, unsigned char* yuv_nv12_data) { int width = mat.cols; int height = mat.rows; // Convertir a YUV cv::Mat yuv; cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV); // Obtener los datos de YUV int y_size = width * height; int uv_size = y_size / 2; unsigned char* y_data = yuv_nv12_data; unsigned char* uv_data = yuv_nv12_data + y_size; // Copiar el plano Y memcpy(y_data, yuv.data, y_size); // Copiar los planos U y V (intercalados) for (int i = 0; i < uv_size; i++) { uv_data[2 * i] = yuv.data[y_size + i * 2]; uv_data[2 * i + 1] = yuv.data[y_size + i * 2 + 1]; } } ``` La función recibe como parámetros la imagen en formato cv::Mat y un puntero a un array de bytes donde se almacenará la imagen en formato yuv_nv12. Primero se convierte la imagen a formato YUV utilizando la función cv::cvtColor(). Luego se copian los planos Y, U y V al array de bytes, respetando el orden intercalado que tiene el formato yuv_nv12. Para utilizar esta función, se puede llamar de la siguiente manera: ``` cv::Mat image = cv::imread("imagen.jpg"); unsigned char* yuv_nv12_data = new unsigned char[image.cols * image.rows * 3 / 2]; mat2yuv_nv12(image, yuv_nv12_data); ``` Se carga la imagen en formato cv::Mat utilizando la función cv::imread(), y se crea un array de bytes donde se almacenará la imagen en formato yuv_nv12. Luego se llama a la función mat2yuv_nv12() para convertir la imagen y almacenarla en el array de bytes.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值