数据压缩3 | 色彩空间转换

一 实验目的

1.学会从计算和程序的角度分析问题通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化为可用程序方式解决的问题。在此过程中设计出一个解决方案。
2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。
3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进行下采样,文件读写过程等。掌握程序调试的基本方法。

 

二 知识储备

1 RGB与YUV格式文件存储结构

详见:https://blog.csdn.net/qq_35613190/article/details/114592508?spm=1001.2014.3001.5501

2 亮度方程与色差信号

\left\{ \begin{aligned} Y & = & \0.2990R + 0.5870G+0.1140B \\ R-Y & = & 0.7010R-0.5870G-0.1140B \\ B-Y & = & -0.2990R-0.5870G+0.8860B \end{aligned} \right.

为了使色差信号的动态范围控制在0.5之间,对色差信号引入压缩系数,进行归一化,得到模拟分量(此处严格来说指代Cb和Cr,但用U、V表示)

\left\{ \begin{aligned} U & = & \0.564(B-Y)=-0.1684R-0.3316+0.5B \\ V & = & \0.713(R-Y)=0.5R-0.4187G-0.0813B\\ \end{aligned} \right.

为了使量化后的色差信号非负,还需加上偏置,即

\begin{bmatrix} Y\\ U\\ V\\ \end{bmatrix}=\begin{bmatrix} 0.2990 & 0.5870 & 0.1140\\ -0.1684 & -0.3316& 0.5000\\ 0.5000& -0.4187& -0.0813 \end{bmatrix}\begin{bmatrix} R\\ G\\ B \end{bmatrix}+\begin{bmatrix} 0\\ 128\\ 128 \end{bmatrix}

经矩阵运算后可得YUV转RGB公式(偏置经过规整)

\begin{bmatrix} R\\ G\\ B\\ \end{bmatrix}=\begin{bmatrix} 1.0000 & 0.0000 & 1.4020\\ 1.0000 & -0.3440& -0.7135\\ 1.0000& 1.7710& 0.0037 \end{bmatrix}\begin{bmatrix} Y\\ U\\ V \end{bmatrix}+\begin{bmatrix} -180\\ 135\\ 228 \end{bmatrix}

三 实验步骤及实验代买(YUV转RGB)

1 读取文件参数及分配缓存区 | 主函数

本实验采取了main函数调用命令参数的方式(argc、argv),传递的参数在项目中手动输入,从而使得程序精简。

int main(int argc, char* argv[])
{

    int height, width, size;
    char* yuvPath = NULL, * rgbPath = NULL;//路径
    height = atoi(argv[3]);
    width = atoi(argv[4]);
    size = height * width;

    yuvPath = argv[1];
    rgbPath = argv[2];

    unsigned char* yuvBuf, * rgbBuf;
    yuvBuf = (unsigned char*)malloc(sizeof(unsigned char) * size * 1.5);
    rgbBuf = (unsigned char*)malloc(sizeof(unsigned char) * size * 3);
    
    InitLookupTable();
    
    //读取文件
    FILE* yuvFile = NULL, * rgbFile = NULL;//文件指针
    errno_t err;
    if ((err = fopen_s(&yuvFile, yuvPath, "rb")) != 0)cout << "FAIL TO OPEN!";
    fread(yuvBuf, sizeof(unsigned char), size * 1.5, yuvFile);

    YUV2RGB(width, height, yuvBuf, rgbBuf);//转换函数

    //写入结果
    if ((err = fopen_s(&rgbFile, rgbPath, "wb")) != 0)cout << "FAIL TO OPEN!";

    fwrite(rgbBuf, sizeof(unsigned char), size * 3, rgbFile);

    //收尾工作
    free(rgbBuf);
    free(yuvBuf);
    fclose(rgbFile);
    fclose(yuvFile);
    return 0;

}

2 格式转换 | 转换函数

void YUV2RGB(int x_dim, int y_dim, unsigned char* yuvBuf, unsigned char* rgbBuf) {
    int size = x_dim * y_dim;
    unsigned char* pr, * pg, * pb;
    unsigned char* pu1, * pu2, * pv1, * pv2, * psu, * psv;//psy,psu,puv为原yuv文件中的起始地址
    unsigned char*uBuf, * vBuf;//为上采样分配缓存区
    unsigned char* py, * pu, * pv;
    uBuf = (unsigned char*)malloc(sizeof(unsigned char) * size);
    vBuf = (unsigned char*)malloc(sizeof(unsigned char) * size);
    float tmpr, tmpg, tmpb;//计算后的临时变量
}

2.1 上采样

原yuv格式为4:2:0采样格式,而对应的rgb格式rgb采样数量相同,故需将yuv上变换到4:4:4后再进行转换

//上采样
    for (int j = 0; j < y_dim / 2; j++) {
        psu = yuvBuf + size + j * x_dim / 2;
        psv = yuvBuf + size + size / 4 + j * x_dim / 2;
        pu1 = uBuf + 2 * j * x_dim;//缓存区偶数行起始
        pu2 = uBuf + (2 * j + 1) * x_dim;//奇数行
        pv1 = vBuf + 2 * j * x_dim;
        pv2 = vBuf + (2 * j + 1) * x_dim;
        for (int i = 0; i < x_dim / 2; i++) {
            //u上采样,赋值至右下右下三个点
            *pu1 = *psu; *(pu1 + 1) = *psu;
            *pu2 = *psu; *(pu2 + 1) = *psu;
            //v
            *pv1 = *psv; *(pv1 + 1) = *psv;
            *pv2 = *psv; *(pv2 + 1) = *psv;

            psu++; psv++;
            pu1 += 2; pu2 += 2;
            pv1 += 2; pv2 += 2;
        }
    }

2.2 yuv2rgb 转换

 //转换格式 以下
    py = yuvBuf;
    pu = uBuf;
    pv = vBuf;
    pb = rgbBuf;

    for (int i = 0; i < size; i++) {
        pg = pb + 1;//rgb文件排列为BGRBGRBGR...
        pr = pb + 2;
        tmpr = *py + YUVRGB14020[*pv] - 180;
        tmpg = *py - YUVRGB03440[*pu] - YUVRGB07135[*pv] + 135;
        tmpb = *py + YUVRGB17710[*pu] - YUVRGB00037[*pv] - 228;

        //溢出检测
        tmpr = tmpr > 255 ? 255 : (tmpr < 0 ? 0 : tmpr);
        tmpg = tmpg > 255 ? 255 : (tmpg < 0 ? 0 : tmpg);
        tmpb = tmpb > 255 ? 255 : (tmpb < 0 ? 0 : tmpb);

        //写入rgb
        *pr = (unsigned char)tmpr;
        *pg = (unsigned char)tmpg;
        *pb = (unsigned char)tmpb;

        py++;
        pu++;
        pv++;
        pb += 3;
    }

为了方便计算以及提高执行速度,建立了系数的查找表:

static float YUVRGB14020[256], YUVRGB03440[256], YUVRGB07135[256];
static float YUVRGB17710[256], YUVRGB00037[256];//查找表

void InitLookupTable()
{
    int i;
    for (i = 16; i < 241; i++) YUVRGB03440[i] = (float)0.3440 * i;
    for (i = 16; i < 241; i++) YUVRGB07135[i] = (float)0.7135 * i;
    for (i = 16; i < 241; i++) YUVRGB14020[i] = (float)1.4020 * i;
    for (i = 16; i < 241; i++) YUVRGB17710[i] = (float)1.7710 * i;
    for (i = 16; i < 241; i++) YUVRGB00037[i] = (float)0.0037 * i;
}

三 实验结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值