1.实验目的
将 YUV 转换为 RGB 的程序。将给定的实验数据用该程序转换为 RGB 文件。 并与原 RGB 文件进行比较, 如果有误差,分析误差来自何处。
2.实验原理
本实验中将256 * 256 YUV文件转化为256 * 256 RGB文件,对应关系为:
B=Y+1.773U-226.944
R=Y+1.403V-179.584
G=Y-0.714V-0.344U+135.424
而实验中YUV文件为4:2:0采样,这样就导致Y与UV不是1:1对应的,在转化时采取了这样的处理具体操作在代码中有标注
for (i = 0; i < x_dim; i++) {
/*以下对应找到RGB的位置,在RBG文件中,数据是
BGRBGRBGR...储存,因此确定第一个B,以此来找到G和R。*/
g = b + 1;
r = b + 2;
/*此处的处理是,找到一个Y,然后寻找对应的U和V,这种方法在人脑逻
辑上更容易操作,但是并不是最好的程序*/
u = y + x_dim * y_dim - (i + 1) / 2 - j * 2 * x_dim / 2 + (j + 1) / 2 * x_dim / 2;
v = y + x_dim * y_dim + x_dim / 2 * y_dim / 2 - (i + 1) / 2 - j * 2 * x_dim / 2 + (j + 1) / 2 * x_dim / 2;
//此处代码省略,原代码进行转换运算,在下面单独进行了说明
‘yuv2rgb’
//以下接收数据
*r = rr;
*g = gg;
*b = bb;
//Y向后移1
y++;
//B向后移3指向下一个B
b += 3;
}
在转换过程中,为避免出现像素越界,这里直接将越界的值置为最小值0(最大值255)
int rr, gg, bb;
rr = (int)(*y + R_V01403[*v]);
gg = (int)(*y + G_U_0344[*u] + G_V_0714[*v]);
bb = (int)(*y + B_U01773[*u]);
//此处为了避免像素值越界,直接将越界的像素值置为最小值0(最大值255)
if (rr < 0) rr = 0;
if (rr > 255) rr = 255;
if (gg < 0) gg = 0;
if (gg > 255) gg = 255;
if (bb < 0) bb = 0;
if (bb > 255) bb = 255;
3.程序实现
完整程序
YUV2RGB.cpp
/*
B=Y+1.773U-226.944
R=Y+1.403V-179.584
G=Y-0.714V-0.344U+135.424
*/
#include "stdlib.h"
#include "yuv2rgb.h"
static float R_V01403[256], B_U01773[256], G_U_0344[256], G_V_0714[256];
int YUV2RGB(int x_dim, int y_dim, void* bmp, void* rgb_out)
{
static int init_done = 0;
long i, j, size;
unsigned char* r, * g, * b;
unsigned char* y, * u, * v;
//这里添加了示例中的检测,此处YUV4:2:0采样,如果此处不能被2整除,UV分量的转化将无法正常进行
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
//创建查找表
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
y = (unsigned char*)bmp;
b = (unsigned char*)rgb_out;
//此处进行运算
for (j = 0; j < y_dim; j++)
{
for (i = 0; i < x_dim; i++) {
g = b + 1;
r = b + 2;
u = y + x_dim * y_dim - (i + 1) / 2 - j * 2 * x_dim / 2 + (j + 1) / 2 * x_dim / 2;
v = y + x_dim * y_dim + x_dim / 2 * y_dim / 2 - (i + 1) / 2 - j * 2 * x_dim / 2 + (j + 1) / 2 * x_dim / 2;
int rr, gg, bb;
rr = (int)(*y + R_V01403[*v]);
gg = (int)(*y + G_U_0344[*u] + G_V_0714[*v]);
bb = (int)(*y + B_U01773[*u]);
//此处为了避免像素值越界,直接将越界的像素值置为最小值0(最大值255)
if (rr < 0) rr = 0;
if (rr > 255) rr = 255;
if (gg < 0) gg = 0;
if (gg > 255) gg = 255;
if (bb < 0) bb = 0;
if (bb > 255) bb = 255;
*r = rr;
*g = gg;
*b = bb;
y++;
b += 3;
}
}
return 0;
}
//查找表
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) R_V01403[i] = (float)1.403 * (i - 128);
for (i = 0; i < 256; i++) B_U01773[i] = (float)1.773 * (i - 128);
for (i = 0; i < 256; i++) G_U_0344[i] = (float)-0.344 * (i - 128);
for (i = 0; i < 256; i++) G_V_0714[i] = (float)-0.714 * (i - 128);
}
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "yuv2rgb.h"
#define u_int8_t unsigned __int8
#define u_int unsigned __int32
#define FALSE false
#define TRUE true
int main(int argc, char** argv)
{
u_int frameWidth = 352;
u_int frameHeight = 240;
bool flip = TRUE;
unsigned int i;
char* rgbFileName = NULL;
char* yuvFileName = NULL;
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
u_int8_t* yuvBuf = NULL;
u_int8_t* rgbBuf = NULL;
yuvFileName = argv[1];
rgbFileName = argv[2];
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
//打开yuv文件
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);
}
//准备rgb文件
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 = (u_int8_t*)malloc(frameWidth * frameHeight * 1.5);
rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
//错误检测
if (yuvBuf == NULL || rgbBuf == NULL)
{
printf("no enough memory\n");
exit(1);
}
while (fread(yuvBuf, 1, frameWidth * frameHeight * 1.5, yuvFile))
{
if (YUV2RGB(frameWidth, frameHeight, yuvBuf, rgbBuf))
{
printf("error");
return 0;
}
fwrite(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile);
}
fclose(rgbFile);
fclose(yuvFile);
return(0);
}
头文件
#pragma once
int YUV2RGB(int x_dim, int y_dim, void* bmp, void* rgb_out);
void InitLookupTable();
4.处理结果
YUV原图
处理后对比图,左侧为转换得到的图,右侧为RGB例图
5.误差分析
在YUV转RGB时,由于YUV采用的是4:2:0量化,色度信号与原RGB不是一一对应的,且在处理时对溢出数据直接赋为最大(最小)值,都会产生误差,但在最终结果可以看出,本实验中产生的误差不明显影响人眼视觉 体验