Yuv数据简介 与压缩处理

YUV:

YUV,是一种颜色编码方法。常使用在各个影像处理组件中。 YUV在对照片或影片编码时,考虑到人类的感知能力,允许降低色度的带宽。

YUV是编译true-color颜色空间(color space)的种类,Y'UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma),

Y′UV, YUV, YCbCr, YPbPr所指涉的范围,常有混淆或重叠的情况。从历史的演变来说,其中YUV和Y'UV通常用来编码电视的模拟信号,而YCbCr则是用来描述数字的影像信号,适合影片与图片压缩以及传输,例如MPEG、JPEG。 但在现今,YUV通常已经在电脑系统上广泛使用。

与RGB格式(红-绿-蓝)不同,YUV格式用一个称为Y(相当于灰度)的“亮度”分量和两个“色度”分量表示,分别称为U(蓝色投影)和V(红色投影)。             

YUV Formats分成两个格式:

紧缩格式(packed formats):将Y、U、V值存储成Macro Pixels数组,和RGB的存放方式类似。
平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。

紧缩格式:

紧缩格式(packed format)中的YUV是混合在一起的,对于YUV4:2:2格式而言,用紧缩格式很合适的,因此就有了UYVY、YUYV等。

UYVY(YUV422)

平面格式:

平面格式(planar formats)是指每Y分量,U分量和V分量都是以独立的平面组织的,也就是说所有的U分量必须在Y分量后面,而V分量在所有的U分量后面,此一格式适用于采样(subsample)。平面格式(planar format)有I420(4:2:0)、YV12、IYUV、 NV21、 NV12等。平面格式较为常用

NV21

NV12

   

与RGB转换关系

对于同一个观测像素点,在RGB空间和YUV空间可进行了不同角度的表示,两者满足单射线性变换,两者可以通过线性变换公式进行转换,如图1所示

转换公式如下:

            Y = 0.299R + 0.587G + 0.114B

            U= -0.169R - 0.331G + 0.500B + 128

            V= 0.500R - 0.419G - 0.081B + 128

反向从YUV转换到RGB的公式为:

          R= Y + 1.402 (V − 128 )

          G= Y − 0.344136 (U − 128)− 0.714136 (V − 128)

          B= Y + 1.772(U − 128)​

YUV转RGB C语音代码:

#include <stdio.h>
#include <stdlib.h>

// YUV到RGB的转换公式
void YUV422_to_RGB888(int width, int height, unsigned char *yuv_buffer, unsigned char *rgb_buffer) {
    int y, u, v, r, g, b;
    for (int i = 0; i < width * height * 2; i += 4) {
        // 获取YUV分量
        u = yuv_buffer[i] - 128;
        y = yuv_buffer[i + 1];
        v = yuv_buffer[i + 2] - 128;
        // 第一个像素的RGB转换
        r = y + (1.370705 * v);
        g = y - (0.698001 * v) - (0.337633 * u);
        b = y + (1.732446 * u);
        // 确保RGB值在0-255范围内
        r = r < 0 ? 0 : r > 255 ? 255 : r;
        g = g < 0 ? 0 : g > 255 ? 255 : g;
        b = b < 0 ? 0 : b > 255 ? 255 : b;
        // 将RGB值写入缓冲区
        rgb_buffer[(i / 2) * 3] = r;
        rgb_buffer[(i / 2) * 3 + 1] = g;
        rgb_buffer[(i / 2) * 3 + 2] = b;
        // 获取下一个Y分量
        y = yuv_buffer[i + 3];
        // 第二个像素的RGB转换
        r = y + (1.370705 * v);
        g = y - (0.698001 * v) - (0.337633 * u);
        b = y + (1.732446 * u);
        // 确保RGB值在0-255范围内
        r = r < 0 ? 0 : r > 255 ? 255 : r;
        g = g < 0 ? 0 : g > 255 ? 255 : g;
        b = b < 0 ? 0 : b > 255 ? 255 : b;
        // 将RGB值写入缓冲区
        rgb_buffer[(i / 2) * 3 + 3] = r;
        rgb_buffer[(i / 2) * 3 + 4] = g;
        rgb_buffer[(i / 2) * 3 + 5] = b;
    }
}

void RGB888_to_YUV422(int width, int height, unsigned char *rgb_buffer, unsigned char *yuv_buffer) {
    int y0, u, y1, v;
    for (int i = 0; i < width * height * 3; i += 6) {
        // 获取RGB分量
        unsigned char r0 = rgb_buffer[i];
        unsigned char g0 = rgb_buffer[i + 1];
        unsigned char b0 = rgb_buffer[i + 2];
        unsigned char r1 = rgb_buffer[i + 3];
        unsigned char g1 = rgb_buffer[i + 4];
        unsigned char b1 = rgb_buffer[i + 5];

        // 转换第一个像素的Y分量
        y0 = (77 * r0 + 150 * g0 + 29 * b0) >> 8;
        // 转换U分量
        u = ((-43 * r0 - 85 * g0 + 128 * b0) >> 8) + 128;
        // 转换第二个像素的Y分量
        y1 = (77 * r1 + 150 * g1 + 29 * b1) >> 8;
        // 转换V分量
        v = ((128 * r0 - 107 * g0 - 21 * b0) >> 8) + 128;

        // 将YUV分量写入缓冲区
        yuv_buffer[i / 3 * 2] = u;
        yuv_buffer[i / 3 * 2 + 1] = y0;
        yuv_buffer[i / 3 * 2 + 2] = v;
        yuv_buffer[i / 3 * 2 + 3] = y1;
    }
}

int main() {
    // 假设yuv_buffer是包含YUV数据的缓冲区
    unsigned char *yuv_buffer = malloc(1920 * 1080 * 2); // YUV 4:2:2
    // 假设rgb_buffer是用于存储RGB数据的缓冲区
    unsigned char *rgb_buffer = malloc(1920 * 1080 * 3); // RGB 8:8:8

    // 填充yuv_buffer数据...
    // ...

    // 转换YUV到RGB
    YUV422_to_RGB888(1920, 1080, yuv_buffer, rgb_buffer);
    //RGB888_to_YUV422(1920, 1080, rgb_buffer, yuv_buffer);
    // 使用rgb_buffer数据...
    // ...

    // 释放内存
    free(yuv_buffer);
    free(rgb_buffer);

    return 0;
}

yuv422缩放:


void RGB888_to_YUV422(int width, int height, unsigned char *rgb_buffer, unsigned char *yuv_buffer) {
    int y0, u, y1, v;
    for (int i = 0; i < width * height * 3; i += 6) {
        // 获取RGB分量
        unsigned char r0 = rgb_buffer[i];
        unsigned char g0 = rgb_buffer[i + 1];
        unsigned char b0 = rgb_buffer[i + 2];
        unsigned char r1 = rgb_buffer[i + 3];
        unsigned char g1 = rgb_buffer[i + 4];
        unsigned char b1 = rgb_buffer[i + 5];

        // 转换第一个像素的Y分量
        y0 = (77 * r0 + 150 * g0 + 29 * b0) >> 8;
        // 转换U分量
        u = ((-43 * r0 - 85 * g0 + 128 * b0) >> 8) + 128;
        // 转换第二个像素的Y分量
        y1 = (77 * r1 + 150 * g1 + 29 * b1) >> 8;
        // 转换V分量
        v = ((128 * r0 - 107 * g0 - 21 * b0) >> 8) + 128;

        // 将YUV分量写入缓冲区
        yuv_buffer[i / 3 * 2] = u;
        yuv_buffer[i / 3 * 2 + 1] = y0;
        yuv_buffer[i / 3 * 2 + 2] = v;
        yuv_buffer[i / 3 * 2 + 3] = y1;
    }
}

图像压缩:

一般可以使用libyuv进行压缩,如果项目上存储容量不够可以使用下面比较简单的转换做压缩处理

yuv422(UYVY)

1760*1280转1280*720 由于每两个y分量共用一组uv色差值,所以视为宽880 高1280 转换宽比值为:11:9 高比值:16:9

C:

#include <stdio.h>
#include <cstring>
int readFile(unsigned char* buf, int len,char*name){
    FILE* fp = NULL;
    fp = fopen(name, "rb+");
    if (fp != NULL) {
        fread((char*)buf, 1, len, fp);
        fclose(fp);
    } 
}

int writeFile(unsigned char* buf, int len,char*name){
    FILE* fp = NULL;
    fp = fopen(name, "wb+");
    if (fp != NULL) {
        fwrite((char*)buf, 1, len, fp);
        fclose(fp);
    } 
}

void RGB888_to_YUV422(int width, int height,  unsigned char *rgb_buffer,  unsigned char *yuv_buffer) {
    int y0, u, y1, v;
    for (int i = 0; i < width * height * 3; i += 6) {
        // 获取RGB分量
        unsigned char r0 = rgb_buffer[i];
        unsigned char g0 = rgb_buffer[i + 1];
        unsigned char b0 = rgb_buffer[i + 2];
        unsigned char r1 = rgb_buffer[i + 3];
        unsigned char g1 = rgb_buffer[i + 4];
        unsigned char b1 = rgb_buffer[i + 5];

        // 转换第一个像素的Y分量
        y0 = (77 * r0 + 150 * g0 + 29 * b0) >> 8;
        // 转换U分量
        u = ((-43 * r0 - 85 * g0 + 128 * b0) >> 8) + 128;
        // 转换第二个像素的Y分量
        y1 = (77 * r1 + 150 * g1 + 29 * b1) >> 8;
        // 转换V分量
        v = ((128 * r0 - 107 * g0 - 21 * b0) >> 8) + 128;

        // 将YUV分量写入缓冲区
        yuv_buffer[i / 3 * 2] = u;
        yuv_buffer[i / 3 * 2 + 1] = y0;
        yuv_buffer[i / 3 * 2 + 2] = v;
        yuv_buffer[i / 3 * 2 + 3] = y1;
    }
}

int main(void){
    int width = 1706;
    int heigh = 1280;
//===========1706 *1279 --> 1706 *1280 ===============
    unsigned char *rgbBuf = new unsigned char[1760*1280*3];
    unsigned char *yuvBuf = new unsigned char[1760*1280*2];
    readFile(rgbBuf,width * 1279 *3,"20240308094827.rgb");
    memcpy(rgbBuf+width * 1279 *3,rgbBuf+width * 1278 *3,width*3);
    writeFile(rgbBuf,width * heigh *3,"1706x1280.rgb");
//=============================================================

//===========1706 *1280 --> 1760 *1280 ===============
    unsigned char *dstBuf = new unsigned char[1760*1280*3];
    int dstH = 0;
    int dstW = 0;
    for(int h=0;h<1280;h++){
        for(int w=0;w<1706;w++){
            dstBuf[((dstW + dstH*1760)*3)]=  rgbBuf[((w+h*width)*3)] ;//r
            dstBuf[((dstW + dstH*1760)*3)+1] = rgbBuf[((w+h*width)*3)+1] ;//g
            dstBuf[((dstW + dstH*1760)*3)+2] = rgbBuf[((w+h*width)*3)+2] ;//b
    
            dstW++;
            if((dstW % 32 == 0) && dstW < 1760){
                
                dstBuf[((dstW + dstH*1760)*3)]=  rgbBuf[((w+h*width)*3)] ;//r
                dstBuf[((dstW + dstH*1760)*3)+1] = rgbBuf[((w+h*width)*3)+1] ;//g
                dstBuf[((dstW + dstH*1760)*3)+2] = rgbBuf[((w+h*width)*3)+2] ;//b
                dstW++;
            }
        }
        dstH++;
        dstW= 0;
    }
    writeFile(dstBuf,1760*1280*3,"1760x1280.rgb");
    memcpy(rgbBuf,dstBuf,1760*1280*3);

   dstW = 0;
   RGB888_to_YUV422(1760, 1280, dstBuf, yuvBuf);
   writeFile(yuvBuf,1760 *1280 *2,"1760x1280.uyuv");
   //===============1760*1280 UYUV --> 1280*720 UYUV=============
   dstH = 0;
   unsigned char u,y,v,r,g,b;
    //高取一半数据
    for(int h=0;h<1280;h+=2){
        //由于y和y1共用一组uv数据,所以宽为2个y数据为一组像素 ,1760/2  ==880
        for(int w=0;w<880;w++){//880   //1280
            //判断宽数据比例11取8,进行数据填充
            if(w % 11 == 0 || w % 11 == 2 || w % 11 == 3 || w % 11 == 4
            || w % 11 == 6 || w % 11 == 7 || w % 11 == 8 || w % 11 == 10
            ){
                dstBuf[(dstW + dstH*640)*4+0] = yuvBuf[(w + h*880)*4+0];//u数据填充
                dstBuf[(dstW + dstH*640)*4+1] = yuvBuf[(w + h*880)*4+1];//y数据填充
                dstBuf[(dstW + dstH*640)*4+2] = yuvBuf[(w + h*880)*4+2];//v数据填充
                dstBuf[(dstW + dstH*640)*4+3] = yuvBuf[(w + h*880)*4+3];//y1数据填充
                dstW++;              
            }
        }
         dstH++;
          dstW= 0;
        //判断高数据比例16比9 ,额外补多一行进行数据填充
        if(h % 16 == 14){
           for(int w=0;w<880;w++){
             //判断宽数据比例11取8,进行数据填充
            if(w % 11 == 0 || w % 11 == 2 || w % 11 == 3 || w % 11 == 4
            || w % 11 == 6 || w % 11 == 7 || w % 11 == 8 || w % 11 == 10
            ){
                dstBuf[(dstW + dstH*640)*4+0] = yuvBuf[(w + h*880)*4+0];//u数据填充
                dstBuf[(dstW + dstH*640)*4+1] = yuvBuf[(w + h*880)*4+1];//y数据填充
                dstBuf[(dstW + dstH*640)*4+2] = yuvBuf[(w + h*880)*4+2];//v数据填充
                dstBuf[(dstW + dstH*640)*4+3] = yuvBuf[(w + h*880)*4+3];//y1数据填充
                dstW++;              
            }
       
        }
           dstH++;
        }
        
        dstW= 0;

    }
    writeFile(dstBuf,1280 *720 *2,"1280x720.uyuv");
}

  

图像效果:

测试代码在附近上

  • 24
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hmbbPdx_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值