一 实验目的
1.学会从计算和程序的角度分析问题通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化为可用程序方式解决的问题。在此过程中设计出一个解决方案。
2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。
3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进行下采样,文件读写过程等。掌握程序调试的基本方法。
二 知识储备
1 RGB与YUV格式文件存储结构
详见:https://blog.csdn.net/qq_35613190/article/details/114592508?spm=1001.2014.3001.5501
2 亮度方程与色差信号
为了使色差信号的动态范围控制在0.5之间,对色差信号引入压缩系数,进行归一化,得到模拟分量(此处严格来说指代Cb和Cr,但用U、V表示)
为了使量化后的色差信号非负,还需加上偏置,即
经矩阵运算后可得YUV转RGB公式(偏置经过规整)
三 实验步骤及实验代买(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;
}