YUVおよびRGB変換
アナログ信号
RGBからYUV
Y = 0.299R + 0.587G + 0.114B
U = 0.493 (BY)=-0.147R-0.289G + 0.436B
V = 0.877 (RY)= 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(BY)
Cr = 0.713(VY)
8ビット量子化では、輝度信号は上端に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;
}
出力結果は次のとおりです。
異なるデータは153,981で、データの約78.3%です。
上記のように、yuvビューアを使用して出力されたyuvファイルを開きます。
分析エラー
- クロマのサンプリング時にエラーが発生する
- rgbとyuvが交換されるときの量子化エラー
- データ変換中にエラーが発生する