YUV和RGB的转换
模拟信号
RGB转YUV
Y=0.299R+0.587G+0.114B
U=0.493(B-Y)=-0.147R-0.289G+0.436B
V=0.877(R-Y)=0.615R-0.515G-0.100B
YUV转RGB
R=Y+1.14V
G=Y-0.394U-0.581V
B=Y+2.032U
数字信号
数字信号中,为了便于处理,需要在量化前做归一化处理
Cb=0.564(B-Y)
Cr=0.713(V-Y)
8bit量化中,亮度信号上端留20级下端留16级作为保护带。
而在色度信号中上端15级下端16级,且色差信号进行归一处理后,电平范围为-0.5~0.5,故色差信号的零电平应对应第128级。
RGB转YCbCr
Y=((66R+129G+25B+128)>>8)+16
Cb=((-38R-74G+112B+128)>>8)+128
Cr=((112R-94G-18B+128)>>8)+128
YCbCr转RGB
R=1.164(Y-16)+1.596(Cr-128)
G=1.164(Y-16)-0.813(Cr-128)-0.392(Cb-128)
B=1.164(Y-16)+2.017(Cb-128)
色度格式
取样格式
常见色度取样格式有4:4:4,4:2:2,4:2:0,4:1:1
这次实验中选择了4:2:2格式
存储格式
先存所有的y,再存所有的u,最后存所有的v,之后进行下一帧
代码
head.h
- 声明所用的函数
#ifndef HEAD_H
#define HEAD_H
#include<iostream>
#include<cstdio>
#include<fstream>
extern int rgb2yuv(char*, char*);
extern int yuv2rgb(char*, char*);
extern int check(char*, char*);
#endif HEAD_H
rgb2yuv.cpp
- 定义将rgb转换为yuv的函数
#include"head.h"
using namespace std;
int rgb2yuv(char*pathin, char*pathout)
{
unsigned char*buffer_1=new unsigned char[256*256*3];
unsigned char*buffer_2 = new unsigned char[256*256];
unsigned char*buffer_3 = new unsigned char[256 * 256];
unsigned char*buffer_4 = new unsigned char[256 * 256];
unsigned char*buffer_5 = new unsigned char[256 * 128+1];
unsigned char*buffer_6 = new unsigned char[256 * 128+1];
unsigned char y, u, v, r, g, b;
ifstream in(pathin, ios::binary);
ofstream out(pathout, ios::binary);
if (!in.is_open()) { cout << "open failed" << endl; }
in.read((char*)buffer_1, 256 * 256*3);
int pos = 0,y_pos=0;
while (pos<256*256*3)
{
for (auto i : { &b,&g,&r }) { *i = buffer_1[pos++]; }
y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
buffer_2[y_pos] = y;
buffer_3[y_pos] = u;
buffer_4[y_pos] = v;
y_pos++;
}
int samp_pos = 0;
for (int i=0; i < 256 * 256; i++)
{
buffer_5[samp_pos++] = (buffer_3[i] + buffer_3[++i])/2;
}
samp_pos = 0;
for (int i = 0; i < 256 * 256; i++)
{
buffer_6[samp_pos++] = (buffer_4[i] + buffer_4[++i])/2;
}
out.write((char*)buffer_2, 256 * 256);
out.write((char*)buffer_5, 256 * 128);
out.write((char*)buffer_6, 256 * 128);
out.close();
system("pause");
return 0;
}
yuv2rgb.cpp
- 定义将yuv转为rgb的函数
#include"head.h"
using namespace std;
int yuv2rgb(char*pathin, char*pathout)
{
unsigned char*buffer_1=new unsigned char[3];
unsigned char*y = new unsigned char[256*256];
unsigned char*u = new unsigned char[256 * 256];
unsigned char*v = new unsigned char[256 * 256];
unsigned char*u_buf = new unsigned char[256 * 128];
unsigned char*v_buf = new unsigned char[256 * 128];
ifstream in(pathin, ios::binary);
ofstream out(pathout, ios::binary);
if (!in.is_open()) { cout << "open failed" << endl; }
int pos = 0;
in.read((char*)y, 256 * 256);
in.read((char*)u_buf, 256 * 128);
in.read((char*)v_buf, 256 * 128);
for (int i = 0; i < 256 * 256; pos++)
{
u[i++] = u_buf[pos];
u[i++] = u_buf[pos];
}
pos = 0;
for (int i = 0; i < 256 * 256; pos++)
{
v[i++] = v_buf[pos];
v[i++] = v_buf[pos];
}
pos = 0;
unsigned char r, g, b;
while (pos<256*256)
{
int n = 0;
r = 1.164*(y[pos]-16) + 1.596*(v[pos]-128);
g = 1.164*(y[pos]) - 0.392*(u[pos]-128) - 0.813*(v[pos]-128);
b = 1.164*(y[pos]-16) + 2.017*(u[pos]-128);
pos++;
for (auto i : { b,g,r }) { buffer_1[n++]=i; }
out.write((char*)buffer_1, 3);
}
in.close();
out.close();
system("pause");
return 0;
}
check.cpp
- 定义将转换回来的rgb文件与原文件比较并输出不相同数据个数的函数
#include"head.h"
using namespace std;
int check(char*path1, char*path2)
{
ifstream in(path1, ios::binary);
ifstream check(path2, ios::binary);
unsigned char*buffer_1 = new unsigned char[3];
unsigned char*buffer_2 = new unsigned char[3];
int aggregate = 0;
while (!in.eof() && !check.eof())
{
in.read((char*)buffer_1, 3);
check.read((char*)buffer_2, 3);
for (int i = 0; i < 3; i++)
{
if (!(buffer_1[i] == buffer_2[i]))
{
aggregate++;
}
}
}
in.close();
check.close();
cout << aggregate << endl;
system("pause");
return 0;
}
MAIN.cpp
- 定义主函数
//
#include"stdafx.h"
#include"head.h"
using namespace std;
int main()
{
char*path_in=new char[10];
cin.getline(path_in, 20, '\n');
char*path_out=new char[10];
cin.getline(path_out, 20, '\n');
char*path_check = new char[10];
cin.getline(path_check, 20, '\n');
rgb2yuv(path_in, path_out);
yuv2rgb(path_out,path_check);
check(path_in, path_check);
return 0;
}
输出结果如下
可见 有153981个数据不一样,大约是78.3%的数据
用yuv viewer打开输出的yuv文件如上
分析误差
- 色度取样时会出现误差
- rgb和yuv互转时量化会出现误差
- 数据转换时会出现误差