文章目录
一、实验目的
RGB转化为YUV程序,重点掌握函数定义,部分查找表的初始化和调用,缓冲区分配。
将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。
二、实验原理
- rgb转yuv的公式为
Y = 0.2990 R + 0.5870 G + 0.1140 B Y=0.2990R+0.5870G+0.1140B Y=0.2990R+0.5870G+0.1140B
U = − 0.1684 R + 0.3316 G + 0.5 B + 128 U=-0.1684R+0.3316G+0.5B+128 U=−0.1684R+0.3316G+0.5B+128
V = 0.5 R − 0.4187 G − 0.0813 B + 128 V=0.5R-0.4187G-0.0813B+128 V=0.5R−0.4187G−0.0813B+128
至于为什么U,V分量要加128,不加就不对。
-
码电平分配以及数字表达式
对分量信号进行8比特均匀量化时,共分256个量化级,为防止信号变动造成过载,亮度信号在上端留20级,下端留16级作为动态范围的保护带。
色差信号经过归一化处理后,动态范围时-0.5-0.5,让色差零电平对应128,256级的上端留15级,下端留16级作为信号动态范围的保护带。
-
色度格式
4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。
三、实验过程
RGB转YUV
代码思路:
头文件
#pragma once
#include "rgbtoyuv.cpp"
extern int RGB2YUV(int Height, int Width, void* rgb_in, void* y_out, void* u_out, void* v_out)
主函数
#include<iostream>
#define uchar unsigned char
#include "rgb2yuv.cpp"
using namespace std;
int main(int argc,char** argv)
{
int framewidth, frameheight;
char* rgbFilename = NULL;
char* yuvFilename = NULL;
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
//传入参数
rgbFilename = argv[1];
yuvFilename = argv[2];
framewidth = atoi(argv[3]);
frameheight = atoi(argv[4]);
//定义指针
unsigned char* rgbBuf;
unsigned char* yBuf;
unsigned char* uBuf;
unsigned char* vBuf;
//分配空间
rgbBuf = (unsigned char*)malloc(frameheight * framewidth * 3);
yBuf = (unsigned char*)malloc(frameheight * framewidth);
uBuf = (unsigned char*)malloc(frameheight * framewidth / 4);
vBuf = (unsigned char*)malloc(frameheight * framewidth / 4);
//打开文件
fopen_s(&rgbFile,rgbFilename, "rb");
if (rgbFile == NULL)
{
printf("there is error_1");
exit(1);
}
else
{
printf("成功创建存放rgb的文件指针\n");
}
//创建yuv文件
fopen_s(&yuvFile, yuvFilename, "wb");
//读取数据
fread(rgbBuf, sizeof(uchar), frameheight * framewidth * 3, rgbFile);
//rgb2yuv
RGB2YUV(frameheight, framewidth,rgbBuf,yBuf,uBuf,vBuf);
//写入文件
fwrite(yBuf, sizeof(uchar), frameheight * framewidth, yuvFile);
fwrite(uBuf, sizeof(uchar), (frameheight * framewidth)/4, yuvFile);
fwrite(vBuf, sizeof(uchar), (frameheight * framewidth)/4, yuvFile);
//关闭文件
fclose(yuvFile);
fclose(rgbFile);
delete rgbBuf;
delete yBuf;
delete vBuf;
delete uBuf;
return 0;
}
rgb2yuv
#include<malloc.h>
#include<iostream>
#include<fstream>
using namespace std;
#define uchar unsigned char
//声明全局变量
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
void InitLookupTable();
int RGB2YUV(int Height, int Width,void* rgb_in,void* y_out,void* u_out,void* v_out)
{
static int init_done = 0;
uchar* r, * g, * b;
uchar* y, * u, * v;
uchar* pu1,*pu2,* pv1, * pv2, * psu, * psv;
uchar* y_buf, * u_buf, * v_buf;
uchar* sub_u_buf, * sub_v_buf;
//把查找表算了
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
if ((Height % 2) || (Width % 2)) return 1;
//分配内存
y_buf = (uchar*)y_out;
sub_u_buf = (uchar*)u_out;
sub_v_buf = (uchar*)v_out;
u_buf = (uchar*)malloc(Height * Width * sizeof(uchar));
v_buf = (uchar*)malloc(Height * Width * sizeof(uchar));
b = (uchar*)rgb_in;//指针指向传进来rgb文件的地址
y = y_buf; //y指针指向y_buf开启的内存地址
u = u_buf;
v = v_buf;
//rgb2yuv
for (int i = 0; i < Height * Width; i++)
{
//bgrbgrbgrbgr...
g = b + 1;
r = b + 2;
*y = (uchar)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (uchar)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
*v = (uchar)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y++;
u++;
v++;
}
// subsample UV ,UV数据以2*2的方块为单元取平均
for (int j = 0; j < Height / 2; j++)
{
//基本指针
psu = sub_u_buf + j * Width / 2;
psv = sub_v_buf + j * Width / 2;
//移动指针,操作当前数据
pu1 = u_buf + 2 * j * Width;
pu2 = u_buf + (2 * j + 1) * Width;
pv1 = v_buf + 2 * j * Width;
pv2 = v_buf + (2 * j + 1) * Width;
for (int i = 0; i < Width / 2; i++)
{
//*psu->sub_u_buf->u_out
*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;
}
}
}
void InitLookupTable()
{
for (int i = 0; i < 256; i++)
{
RGBYUV02990[i] = (float)0.2990 * i;
RGBYUV05870[i] = (float)0.5870 * i;
RGBYUV01140[i] = (float)0.1140 * i;
RGBYUV01684[i] = (float)0.1684 * i;
RGBYUV03316[i] = (float)0.3316 * i;
RGBYUV04187[i] = (float)0.4187 * i;
RGBYUV00813[i] = (float)0.0813 * i;
}
}
解释:
1.本实验采用命令行传参,就是本地rgb文件不是以代码中赋进去的,而是调用int main(int argc,char** argv)中的参数。
2.刚开始想验证打印命令行传参的第二个数组元素是否与上图一致,输出格式不对
3.查找表的使用,提高运行效率
四、实验总结
1.学会用指针去操作数据,以前是用的数组。
2.思考:
- 用指针和用数组有啥不同点?
- 指针的话它需要位置的移动 取值是两个不同的事情。
- 数组的话移到哪里取哪里。