一、原理分析
-
由现代电视原理知识可知亮度和色差计算公式为:
Y=0.2990R+0.5870G+0.1140B
R-Y=0.7010R-0.5870G-0.1140B
B-Y=-0.2990R-0.5870G+0.8860B -
为使色差信号的动态范围控制在-0.5~+0.5之间,要对色差信号进行归一化处理,引入压缩系数后可以得到U、V的计算公式:
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B
此时得到的U、V范围在-128~127之间,为了避免出现负数,加上128。 -
综合得到RGB2YUV最终的计算公式为:
Y=0.2990R+0.5870G+0.1140B
U=-0.1684R-0.3316G+0.5B+128
V=0.5R-0.4187G-0.0813B+128
反推也可以得到YUV2RGB的公式:
R=Y+1.4075(V-128)
G=Y-0.3455(U-128)-0.7169(V-128)
B=Y+1.7790(U-128)
二、RGB2YUV
- 编写RGB2YUV的转换函数
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out);
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buf, *u_buf, *v_buf;
unsigned char *sub_u_buf, *sub_v_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
// 确认图片的宽高为偶数
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
// 给指针分配空间
y_buf = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buf && v_buf))
{
if (u_buf) free(u_buf);
if (v_buf) free(v_buf);
return 2;
}
b = (unsigned char *)bmp;
y = y_buf;
u = u_buf;
v = v_buf;
// convert RGB to YUV
for (j = 0; j < y_dim; j ++)
{
for (i = 0; i < x_dim; i ++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y ++;
u ++;
v ++;
}
}
for (j = 0; j < y_dim; j ++)
{
for (i = 0; i < x_dim; i ++)
{
y = y_buf + (y_dim - j - 1) * x_dim;
u = u_buf + (y_dim - j - 1) * x_dim;
v = v_buf + (y_dim - j - 1) * x_dim;
}
}
// 对UV分量进行下采样
for (j = 0; j < y_dim/2; j ++)
{
psu = sub_u_buf + j * x_dim / 2;
psv = sub_v_buf + j * x_dim / 2;
pu1 = u_buf + 2 * j * x_dim;
pu2 = u_buf + (2 * j + 1) * x_dim;
pv1 = v_buf + 2 * j * x_dim;
pv2 = v_buf + (2 * j + 1) * x_dim;
for (i = 0; i < x_dim/2; i ++)
{
*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;
*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;
psu ++;
psv ++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}
free(u_buf);
free(v_buf);
return 0;
}
- 为提高运算速度定义查找表
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV0_2990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV0_5870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV0_1140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV0_1684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV0_3316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV0_4187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV0_0813[i] = (float)0.0813 * i;
}
- 主函数
int main(int argc, char** argv)
{
int frameWidth;
int frameHeight ;
char* yuvFileName = NULL;
char* rgbFileName = NULL;
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
unsigned char* rgbBuf = NULL;
unsigned char* yBuf = NULL;
unsigned char* uBuf = NULL;
unsigned char* vBuf = NULL;
rgbFileName = argv[1];
yuvFileName = argv[2];
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
//打开RGB文件
fopen_s(&rgbFile,rgbFileName,"rb");
if (rgbFile == NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
else
{
printf("The input rgb file is %s\n", rgbFileName);
}
//打开yuv文件
fopen_s(&yuvFile,yuvFileName,"wb");
if (yuvFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
else
{
printf("The output yuv file is %s\n", yuvFileName);
}
//给BUF分配空间
rgbBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3);
yBuf = (unsigned char*)malloc(frameWidth * frameHeight);
uBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);
vBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);
//
if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
//
fread(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile);
if(rgbBuf == NULL)
{
printf("error");
return 0;
}
//
RGB2YUV(frameWidth , frameHeight , rgbBuf , yBuf , uBuf ,vBuf);
//写入yuv文件
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
//清除
fclose(rgbFile);
fclose(yuvFile);
return(0);
}
- 运行结果:
成功实现了RGB2YUV的转换。
三、YUV2RGB
- 编写YUV2RGB的转换函数
int YUV2RGB(int x_dim, int y_dim, void *ychunk, void *uchunk, void *vchunk, void *rgb_out, int flip)
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *addu_buffer, *addv_buffer;
unsigned char *addu_buf, *addv_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
// check to see if x_dim and y_dim are divisible by 2
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
// allocate memory
addu_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
addv_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
b = (unsigned char *)rgb_out;
y = (unsigned char *)ychunk;
u = (unsigned char*)uchunk;
v = (unsigned char*)vchunk;
addu_buf = (unsigned char *)addu_buffer;
addv_buf = (unsigned char *)addv_buffer;
// 对UV块进行上采样
for (j = 0; j < y_dim/2; j++)
{
psu = u + j * x_dim / 2;//原始数据U块指针
psv = v + j * x_dim / 2;//原始数据V块指针
pu1 = addu_buf + 2 * j * x_dim; //U块奇数行首地址
pu2 = addu_buf + (2 * j + 1) * x_dim;//U块偶数行首地址
pv1 = addv_buf + 2 * j * x_dim; //V块奇数行首地址
pv2 = addv_buf + (2 * j + 1) * x_dim;//V块偶数行首地址
for (i = 0; i < x_dim/2; i++)
{
*pu1 = *psu;
*pu2 = *psu;
pu1++;
pu2++;
*pu1 = *psu;
*pu2 = *psu; //U块上采样:“以1作4”
*pv1 = *psv;
*pv2 = *psv;
pv1++;
pv2++;
*pv1 = *psv;
*pv2 = *psv; //V块上采样:“以1作4”
pu1++;
pu2++;
pv1++;
pv2++;
psu++;
psv++;
}
}
// convert YUV to RGB
if (!flip) {
for (j = 0; j < y_dim; j++)
{
y = y + (y_dim - j - 1) * x_dim;
addu_buf = addu_buf + (y_dim - j - 1) * x_dim;
addv_buf = addv_buf + (y_dim - j - 1) * x_dim;
for (i = 0; i < x_dim; i++) {
g = b + 1;
r = b + 2;
adjust(b, g, r, y, addu_buf, addv_buf);
b += 3;
y++;
addu_buf++;
addv_buf++;
}
}
}
else {
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
adjust(b, g, r, y, addu_buf, addv_buf);//对变量范围作适应性调整
b += 3;
y++;
addu_buf++;
addv_buf++;
}
}
free(addu_buffer);
free(addv_buffer);
return 0;
}
- 为提高运算速度定义查找表
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) YUVRGB1_4075[i] = (float)1.4075 * i;
for (i = 0; i < 256; i++) YUVRGB0_3455[i] = (float)0.3455 * i;
for (i = 0; i < 256; i++) YUVRGB0_7169[i] = (float)0.7169 * i;
for (i = 0; i < 256; i++) YUVRGB1_7790[i] = (float)1.7790 * i;
}
- 主函数
int main(int argc, char** argv)
{
// variables controlable from command line
int frameWidth = 256;
int frameHeight = 256;
unsigned int i;
// internal variables
char* rgbFileName = NULL;
char* yuvFileName = NULL;
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
unsigned char* rgbBuf = NULL;
unsigned char* yuvBuf = NULL;
unsigned char* yBuf = NULL;
u_int32_t videoFramesWritten = 0;
yuvFileName = argv[1];
rgbFileName = argv[2];
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
// open the YUV file
yuvFile = fopen(yuvFileName, "rb");
if (yuvFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
else
{
printf("The input yuv file is %s\n", yuvFileName);
}
// open the RAW file
rgbFile = fopen(rgbFileName, "wb");
if (rgbFile == NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
else
{
printf("The output rgb file is %s\n", rgbFileName);
}
yuvBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3/2);
rgbBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3);
yBuf = yuvBuf;
if (yuvBuf == NULL || rgbBuf == NULL || yBuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
fread(yuvBuf, sizeof(unsigned char), frameWidth * frameHeight * 3/2, yuvFile);
YUV2RGB(frameWidth, frameHeight, yBuf,rgbBuf);
if (rgbBuf==NULL)
{
printf("wrong change\n");
}
fwrite(rgbBuf, sizeof(unsigned char), frameWidth * frameHeight * 3, rgbFile);
//printf("\r...%d", ++videoFramesWritten);
printf("\n%u %ux%u video frames written\n",
videoFramesWritten, frameWidth, frameHeight);
fclose(rgbFile);
fclose(yuvFile);
return(0);
}
- 实验结果截图