數據壓縮(五)——彩色空間轉換(完整版)

實驗一:

  • 編寫RGB轉化爲YUV程序,重點掌握函數定義,部分查找表的初始化和調用,緩衝區分配。將得到的RGB文件轉換爲YUV文件,用YUV Viewer播放器觀看,驗證是否正確。

  • 編寫將YUV轉換爲RGB的程序。將給定的實驗數據用該程序轉換爲RGB文件。並與原

    RGB文件進行比較,如果有誤差,分析誤差來自何處。

  之前寫過一版單純4444:4:4RGBRGB文件與4:4:44:4:4YUVYUV文件的轉換,鏈接如下,此版文章在前文的基礎上進行了轉換公式的推導以及採樣的實現,若是隻關注兩種類型的轉換可以直接去不採樣版查看代碼及思路。

數據壓縮(四)——彩色空間轉換(不採樣版)

(一)YUV和RGB的轉換公式及文件存儲格式

R=(298×Y+411×V57376)>>8G=(298×Y101×U211×V+35168)>>8B=(298×Y+519×U71200)>>8Y=(66×R+129×G+25×B)>>8+16U=(38×R74×G+112×B)>>8+128V=(112×R94×G18×B)>>8+128 \begin{aligned} &R=(298\times Y+411\times V-57376)>>8\\ &G=(298\times Y-101\times U-211\times V+35168)>>8\\ &B=(298\times Y+519\times U-71200)>>8\\ &Y=(66\times R+129\times G+25\times B)>>8+16\\ &U=(-38\times R-74\times G+112\times B)>>8+128\\ &V=(112\times R-94\times G-18\times B)>>8+128 \end{aligned}

  以上公式是已經量化過的公式。

1.1 轉換公式推導

1.1.1 模擬信號的轉換公式

  根據電視原理的知識,我們可以知道得到如下公式:
Y=0.2990×R+0.5870×G+0.1140×BU=BY=0.2990×R0.5870×G+0.8860×BV=RY=0.7010×R0.5870×G0.1140×B \begin{aligned} &Y=0.2990\times R+0.5870\times G+0.1140\times B\\ &U=B-Y=-0.2990\times R-0.5870\times G+0.8860\times B\\ &V=R-Y=0.7010\times R-0.5870\times G-0.1140\times B \end{aligned}

1.1.2 數字信號的轉換公式

歸一化

  爲了便於處理,模擬信號轉換爲數字信號的時候需要進行歸一化處理,使得色差信號的動態範圍控制在0.50.5-0.5\sim0.5之間。

  歸一話之後得到公式:
U=0.564×(BY)=0.564×(0.2990×R0.5870×G+0.8860×B)V=0.713×(RY)=0.713×(0.7010×R0.5870×G0.1140×B) \begin{aligned} &U'=0.564\times (B-Y)=0.564\times (-0.2990\times R-0.5870\times G+0.8860\times B)\\ &V'=0.713\times (R-Y)=0.713\times (0.7010\times R-0.5870\times G-0.1140\times B) \end{aligned}
  由於上述UVU'、V'的取值範圍在±350mv\pm 350mv之間,所以需要引入+350mv+350mv的偏置。公式變爲:
U=0.564×(BY)+0.35V=0.713×(RY)+0.35 \begin{aligned} &U''=0.564\times (B-Y)+0.35\\ &V''=0.713\times (R-Y)+0.35 \end{aligned}

亮度信號量化後碼電平分配

  亮度信號量化後的碼電平分配如圖所示(圖源《數字電視廣播原理與應用》P36):

在這裏插入圖片描述

  可見,亮度信號的峯值白電平對應碼電平235,消隱電平對應碼電平16,爲了預防信號變動造成過載,上端留20級、下端留16級作爲信號超越動態範圍的保護帶。其中碼電平0和255爲保護電平,是不允許出現在視頻數據流中的,碼字00和FF用於傳送同步信息。亮度信號動態範圍共佔220個量化級。

色差信號量化後碼電平分配

  色差信號的碼電平分配如圖所示,圖源《數字電視廣播原理與應用》P37):

在這裏插入圖片描述

  色差信號CbCrC_b和C_r峯值的峯值白電平對應碼電平240,0電平對應碼電平16,爲了預防信號變動造成過載,上端留15級,下端留16級作爲信號超越動態範圍的保護帶。色差信號動態範圍共佔225個量化級。

碼電平數字表達式

  亮度信號和色差信號量化以後取其最鄰近的整數作爲碼電平值,其數字化表達式爲:
DY=INT[(219Y+16)×2n8]DCB=INT[(224Cb+128)×2n8]DCR=INT[(224Cr+128)×2n8]\begin{aligned} &D_Y=INT[(219Y+16)\times 2^{n-8}]\\ &D_{CB}=INT[(224C_b+128)\times 2^{n-8}]\\ &D_{CR}=INT[(224C_r+128)\times 2^{n-8}]\end{aligned}

  將前面所推得的Y,U,VY,U'',V''帶入上式,可得:
Y=(66×R+129×G+25×B)>>8+16U=(38×R74×G+112×B)>>8+128V=(112×R94×G18×B)>>8+128\begin{aligned} &Y=(66\times R+129\times G+25\times B)>>8+16\\ &U=(-38\times R-74\times G+112\times B)>>8+128\\ &V=(112\times R-94\times G-18\times B)>>8+128\end{aligned}

  將式子寫成矩陣的形式:
[YUV]=1256[661292538741121129418][RGB]+[16128128]\begin{aligned}\begin{bmatrix}Y\\U\\V\end{bmatrix}=\frac{1}{256}\begin{bmatrix}66&129&25\\-38&-74&112\\112&-94&-18\end{bmatrix}\begin{bmatrix}R\\G\\B\end{bmatrix}+\begin{bmatrix}16\\128\\128\end{bmatrix}\end{aligned}

  令矩陣[661292538741121129418]\begin{bmatrix}66&129&25\\-38&-74&112\\112&-94&-18\end{bmatrix}爲矩陣AA,則原式可轉化爲:[RGB]=256A1[Y16U128V128]\begin{aligned}\begin{bmatrix}R\\G\\B\end{bmatrix}=256A^{-1}\begin{bmatrix}Y-16\\U-128\\V-128\end{bmatrix}\end{aligned}

  由於A1A^{-1}的數量級過於小,因此先將AA中的每個元素縮小爲原來的1256×256\frac{1}{256\times 256}得到A1A_1。則上式轉換爲:[RGB]=A11[Y16G128B128]÷256\begin{aligned}\begin{bmatrix}R\\G\\B\end{bmatrix}=A_1^{-1}\begin{bmatrix}Y-16\\G-128\\B-128\end{bmatrix}\div 256\end{aligned}

  將係數取整,可得下式:
R=(298×Y+411×V57376)>>8G=(298×Y101×U211×V+35168)>>8B=(298×Y+519×U71200)>>8\begin{aligned} &R=(298\times Y+411\times V-57376) >>8 \\ &G=(298\times Y-101\times U-211\times V+35168)>>8 \\ &B=(298\times Y+519\times U-71200)>>8 \end{aligned}

1.2 文件的存儲格式

  查閱資料可知,RGBRGB文件的存儲格式爲BGRBGRBGRBGR BGR BGR\cdots,YUVYUV文件的存儲格式爲先存全部的YY,再存全部的UU,最後存全部的VV

(二)main函數的命令行參數

2.1 表示方法

  一個程序的main()main()函數可以包含兩個參數:

  • 第一個參數爲intint類型;
  • 第二個參數爲字符串數組;

  通常情況下,將第一個參數命名爲argcargc,第二個參數爲argvargv。由於字符串數組在函數頭中的聲明可以有兩種形式,所以main()main()函數也有兩種寫法。

  1. main()main()函數寫法一

    int main(int argc, char** argv)
    {
        return 0;
    }
    
  2. main()main()函數寫法二:

    int main(int argc, char* argv[])
    {
        return 0;
    }
    

2.2 使用方法

  • 參數的含義:

      int argc​:表示字符串的數量。argc = 1 + 用戶輸入的字符串數目,argc的值由操作系統自動完成計算,程序員不需要對其進行賦值。

      char argv[]*:存放的是多個字符串,字符串的形式如下:

    argv[0] = 可執行文件的名稱。例如change.exe。(這個字符串不需要用戶輸入,與argc相同,操作系統可自動生成。

    argv[1] = 字符串1

    argv[2] = 字符串2

    argv[3] = 字符串3

    \vdots

  • 編程模式下如何進行參數輸入?

  使用平臺爲VisualStudio2019Visual Studio 2019,需要使用的文件爲down.rgbdown.rgb,需要生成的文件爲up.yuv,cho.rgbup.yuv,cho.rgb,參數輸入的步驟如下圖流程所示:

1.打開上方任務欄調試界面的屬性窗口 在這裏插入圖片描述
2.選擇配置屬性中的調試 在這裏插入圖片描述
3.按照要求修改命令參數 在這裏插入圖片描述

(三)彩色空間轉換(不採樣)代碼初步實現

  由上述知識,可以輕易地進行彩色空間轉換的初步實現。代碼由頭文件rgb2yuv.h,yuv2rgb.hrgb2yuv.h,yuv2rgb.h和源文件main.cpp,rgb2yuv.cpp,yuv2rgb.cppmain.cpp,rgb2yuv.cpp,yuv2rgb.cpp組成。

  解決方案資源管理器如下圖所示:

在這裏插入圖片描述
  實驗代碼如下:

main.cpp

#include <iostream>
#include <cstdio>
#include <fstream>
#include "rgb2yuv.h"
#include "yuv2rgb.h"
using namespace std;

#define size 196608
#define usize 65536
#define vsize 131072
using namespace std;

int main(int argc, char** argv)
{
	ifstream infile(argv[1],ios::binary);
	ofstream outYUV(argv[2], ios::binary);
	ofstream outRGB(argv[3], ios::binary);
	if (!infile) { cout << "error to open file1!" << endl; }
	if (!outYUV) { cout << "error to open file2" << endl; }
	if (!outRGB) { cout << "error to open file3" << endl; }

	unsigned char* in = new unsigned char[size];
	unsigned char* YUV = new unsigned char[size];
	unsigned char* RGB = new unsigned char[size];

	infile.read((char*)in, size);
	rgb2yuv(in,YUV,size, usize, vsize);//第一次轉換
	yuv2rgb(YUV, RGB, usize, vsize);//第二次轉換

	/*for (int i = 0; i < size; i++)
	{
		if (abs(in[i] - RGB[i]) > 5)
			cout << "i=" << i << " in[" << i << "]=" << int(in[i]) << " RGB[" << i << "]=" << int(RGB[i]) << endl;
	}*/
	outYUV.write((char*)YUV, size);
	outRGB.write((char*)RGB, size);
	delete in;
	delete YUV;
	delete RGB;
	infile.close();
	outYUV.close();
	outRGB.close();

	return 0;
}

rgb2yuv.h

#pragma once
void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size,int usize,int vsize);

rgb2yuv.cpp

void rgb2yuv(unsigned char* rgb, unsigned char* yuv,int size,int usize,int vsize)
{
	unsigned char r, g, b, y, u, v;
	int j = 0;
	for (int i = 0;i < size;)
	{
		b = *(rgb + i);
		g = *(rgb + i + 1);
		r = *(rgb + i + 2);
		y = ((66 * r + 129 * g + 25 * b) >> 8) + 16;
		u = ((-38 * r - 74 * g + 112 * b) >> 8) + 128;
		v = ((112 * r - 94 * g - 18 * b) >> 8) + 128;
		*(yuv + j) = y;
		*(yuv + j + usize) = u;
		*(yuv + j + vsize) = v;
		i = i + 3;//每個rgb爲1組
		j++;
	}
}

yuv2rgb.h

#pragma once
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);

yuv2rgb.cpp

#pragma once
#include "yuv2rgb.h"
#include <iostream>
using namespace std;
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	unsigned char r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = *(yuv + i);
		u = *(yuv + i + usize);
		v = *(yuv + i + vsize);
		r = (298 * y + 411 * v - 57344) >> 8;
		g = (298 * y - 101 * u - 211 * v + 34739) >> 8;
		b = (298 * y + 519 * u - 71117) >> 8;
		*(rgb + j) = b;
		*(rgb + j + 1) = g;
		*(rgb + j + 2) = r;
		j = j + 3;
	}
}

實驗結果

down.rgb up.yuv cho.rgb
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述

  其中,down.rgbdown.rgbcho.rgbcho.rgb使用YUVviewerPlusYUVviewerPlus打開的打開方式爲:

在這裏插入圖片描述
  打開的圖像是倒置的圖像(由於bmpbmp圖像格式是倒着存儲的,所以.rgb.rgb圖像用bmpbmp方式打開時會倒)。上述表格中的圖片爲了便於分辨,已經用微信進行過旋轉,但是YUVYUV文件和RGBRGB文件之間依然有鏡像的翻轉,不過不影響觀看與比對。

  up.yuvup.yuv使用YUVviewerPlusYUVviewerPlus打開的方式爲:

在這裏插入圖片描述

  由三幅圖像的對比圖可知,RGBtoYUVRGB to YUV的實驗成功完成,而YUVtoRGBYUVtoRGB的實驗有一點問題,轉出的cho.rgbcho.rgb圖像中有較多紅色的雜點。

(四)實驗錯誤原因分析及代碼修改

錯誤修改

  推斷可得,在進行YUVtoRGBYUVtoRGB的轉換時,得到的RGBRGB三個數據可能超過了unsigned charunsigned\ char類型可以表示的範圍,即可能<0<0或者>255>255

  因此需要對yuv2rgb.cppyuv2rgb.cpp文件進行適當的修正,>255>255的值都直接=255=255,<0<0的值都直接=0=0

  修改後yuv2rgb.cppyuv2rgb.cpp的如下:

#pragma once
#include "yuv2rgb.h"
#include <iostream>
using namespace std;
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = int(*(yuv + i));
		u = int(*(yuv + i + usize));
		v = int(*(yuv + i + vsize));
		r = (298 * y + 411 * v - 57344) >> 8;
		if (r > 255) { r = 255; }
		if (r < 0) { r = 0; }
		g = (298 * y - 101 * u - 211 * v + 34739) >> 8;
		if (g > 255) { g = 255; }
		if (g < 0) { g = 0; }
		b = (298 * y + 519 * u - 71117) >> 8;
		if (b > 255) { b = 255; }
		if (b < 0) { b = 0; }
		*(rgb + j) = unsigned char(b);
		*(rgb + j + 1) = unsigned char(g);
		*(rgb + j + 2) = unsigned char(r);
		j = j + 3;
	}
}

實驗結果

down.rgb up.yuv cho.rgb
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述

  至此,差不多完成了RGBtoYUVRGB to YUVYUVtoRGBYUVtoRGB兩個實驗。

(五)優化代碼(使用查找表的方法)

  利用查找表,對代碼進行了優化。代碼由頭文件yuvrgb.hyuvrgb.h和源文件main.cpp,yuvrgb.cppmain.cpp,yuvrgb.cpp組成。

  解決方案資源管理器如下圖所示:

在這裏插入圖片描述

main.cpp

#include <iostream>
#include <cstdio>
#include <fstream>
#include "yuvrgb.h"
using namespace std;

#define size 196608
#define usize 65536
#define vsize 131072
#define height 256
#define weight 256

//查找表初始化
int* RGBYUV298 = new int[256];
int* RGBYUV411 = new int[256];
int* RGBYUV101 = new int[256];
int* RGBYUV211 = new int[256];
int* RGBYUV519 = new int[256];
int* RGBYUV66 = new int[256];
int* RGBYUV129 = new int[256];
int* RGBYUV25 = new int[256];
int* RGBYUV38 = new int[256];
int* RGBYUV74 = new int[256];
int* RGBYUV112 = new int[256];
int* RGBYUV94 = new int[256];
int* RGBYUV18 = new int[256];

int main(int argc, char** argv)
{
	initLookupTable();

	ifstream infile(argv[1],ios::binary);
	ofstream outYUV(argv[2], ios::binary);
	ofstream outRGB(argv[3], ios::binary);
	if (!infile) { cout << "error to open file1!" << endl; }
	if (!outYUV) { cout << "error to open file2" << endl; }
	if (!outRGB) { cout << "error to open file3" << endl; }

	unsigned char* infi = new unsigned char[size];
	unsigned char* YUVfi = new unsigned char[size];
	unsigned char* RGBfi = new unsigned char[size];

	infile.read((char*)infi, size);	
	rgb2yuv(infi, YUVfi, size, usize, vsize);
	yuv2rgb(YUVfi, RGBfi, usize, vsize);
	outYUV.write((char*)YUVfi, size);
	outRGB.write((char*)RGBfi, size);
	
	fileend(infi,YUVfi,RGBfi);

	infile.close();
	outYUV.close();
	outRGB.close();

	return 0;
}

yuvrgb.h

#pragma once
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);
void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize);
void initLookupTable();
void fileend(unsigned char* infi, unsigned char* YUVfi, unsigned char* RGBfi);

yuvrgb.cpp

#pragma once
#include "yuvrgb.h"
#include <iostream>
using namespace std;

extern int* RGBYUV298;
extern int* RGBYUV411;
extern int* RGBYUV101;
extern int* RGBYUV211;
extern int* RGBYUV519;
extern int* RGBYUV66 ;
extern int* RGBYUV129;
extern int* RGBYUV25 ;
extern int* RGBYUV38 ;
extern int* RGBYUV74 ;
extern int* RGBYUV112;
extern int* RGBYUV94 ;
extern int* RGBYUV18 ;

void initLookupTable()
{
	for (int i = 0; i < 256; i++)
	{
		RGBYUV298[i] = 298 * i;
		RGBYUV411[i] = 411 * i;
		RGBYUV101[i] = 101 * i;
		RGBYUV211[i] = 211 * i;
		RGBYUV519[i] = 519 * i;
		RGBYUV66[i] = 66 * i;
		RGBYUV129[i] = 129 * i;
		RGBYUV25[i] = 25 * i;
		RGBYUV38[i] = 38 * i;
		RGBYUV74[i] = 74 * i;
		RGBYUV112[i] = 112 * i;
		RGBYUV94[i] = 94 * i;
		RGBYUV18[i] = 18 * i;
	}
}
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = int(*(yuv + i));
		u = int(*(yuv + i + usize));
		v = int(*(yuv + i + vsize));
		/*r = (298 * y + 411 * v - 57344) >> 8;*/
		r = (RGBYUV298[y]+ RGBYUV411[v]-57344)>>8;
		if (r > 255) { r = 255; }
		if (r < 0) { r = 0; }
		/*g = (298 * y - 101 * u - 211 * v + 34739) >> 8;*/
		g = (RGBYUV298[y] - RGBYUV101[u] - RGBYUV211[v] + 34739) >> 8;
		if (g > 255) { g = 255; }
		if (g < 0) { g = 0; }
		/*b = (298 * y + 519 * u - 71117) >> 8;*/
		b = (RGBYUV298[y] + RGBYUV519[u] - 71117) >> 8;
		if (b > 255) { b = 255; }
		if (b < 0) { b = 0; }
		*(rgb + j) = unsigned char(b);
		*(rgb + j + 1) = unsigned char(g);
		*(rgb + j + 2) = unsigned char(r);
		j = j + 3;
	}
}

void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < size;)
	{
		b = int(*(rgb + i));
		g = int(*(rgb + i + 1));
		r = int(*(rgb + i + 2));
		/*y = ((66 * r + 129 * g + 25 * b) >> 8) + 16;*/
		y = ((RGBYUV66[r] + RGBYUV129[g] + RGBYUV25[b]) >> 8) + 16;
		/*u = ((-38 * r - 74 * g + 112 * b) >> 8) + 128;*/
		u = ((-RGBYUV38[r] - RGBYUV74[g] + RGBYUV112[b]) >> 8) + 128;
		/*v = ((112 * r - 94 * g - 18 * b) >> 8) + 128;*/
		v = ((RGBYUV112[r] - RGBYUV94[g] - RGBYUV18[b]) >> 8) + 128;
		/*if ((y > 255) || (u > 255) || (v > 255) || (y < 0) || (u < 0) || (v < 0))
		{
			cout << "y=" << y << "u=" << u << "v=" << v << endl;
		}*/
		*(yuv + j) = unsigned char(y);
		*(yuv + j + usize) = unsigned char(u);
		*(yuv + j + vsize) = unsigned char(v);
		i = i + 3;//每個rgb爲1組
		j++;
	}
}

void fileend(unsigned char* infi, unsigned char* YUVfi, unsigned char* RGBfi)
{
	delete infi;
	delete YUVfi;
	delete RGBfi;
	delete	RGBYUV298;
	delete	RGBYUV411;
	delete	RGBYUV101;
	delete	RGBYUV211;
	delete	RGBYUV519;
	delete	RGBYUV66;
	delete	RGBYUV129;
	delete	RGBYUV25;
	delete	RGBYUV38;
	delete	RGBYUV74;
	delete	RGBYUV112;
	delete	RGBYUV94;
	delete	RGBYUV18;
}

實驗結果

down.rgb up.yuv cho.rgb
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述

  至此,完成了4:4:4的RGBRGB文件與4:4:4的YUVYUV文件之間的轉換。

(六)彩色空間轉換(採樣)代碼實現

在這裏插入圖片描述

  如圖所示是4:2:0的色度取樣格式。因此可以得到從4:4:4的YUVYUV文件轉換爲4:2:0的YUVYUV文件的思路,即保留原有的YY分量,UU信號和VV信號都是取奇數行的奇數點。所以在原有的代碼上稍加改動,按照上述思路,添加一個由4:4:4的YUVYUV文件得到4:2:0的YUVYUV文件的函數,可以實現從RGBRGB文件到420YUV4:2:0YUV文件的轉換。

  關於從420YUV4:2:0YUV文件到RGBRGB文件的轉換,由於像素之間的相關性,可以想到,保留原有的YY分量,UVUV分量可以通過複製來得到缺失的UVUV分量,重新轉換成4:4:4的YUVYUV文件。按照上述思路,添加一個由4:2:0的YUVYUV文件得到4:4:4的YUVYUV文件的函數,可以實現從420YUV4:2:0YUV文件到RGBRGB文件的轉換。

  實驗代碼如下:

main.cpp

#include <iostream>
#include <cstdio>
#include <fstream>
#include "yuvrgb.h"
using namespace std;

#define size 196608
#define csize 98304
#define usize 65536
#define vsize 131072
#define height 256
#define weight 256

//查找表初始化
int* RGBYUV298 = new int[256];
int* RGBYUV411 = new int[256];
int* RGBYUV101 = new int[256];
int* RGBYUV211 = new int[256];
int* RGBYUV519 = new int[256];
int* RGBYUV66 = new int[256];
int* RGBYUV129 = new int[256];
int* RGBYUV25 = new int[256];
int* RGBYUV38 = new int[256];
int* RGBYUV74 = new int[256];
int* RGBYUV112 = new int[256];
int* RGBYUV94 = new int[256];
int* RGBYUV18 = new int[256];

int main(int argc, char** argv)
{
	initLookupTable();

	ifstream infile(argv[1],ios::binary);
	ofstream outYUV444(argv[2], ios::binary);
	ofstream outYUV420(argv[3], ios::binary);
	ofstream outYUV4442(argv[4], ios::binary);
	ofstream outRGB(argv[5], ios::binary);
	if (!infile) { cout << "error to open file1!" << endl; }
	if (!outYUV444) { cout << "error to open file2" << endl; }
	if (!outYUV420) { cout << "error to open file3" << endl; }
	if (!outYUV4442) { cout << "error to open file4" << endl; }
	if (!outRGB) { cout << "error to open file5" << endl; }

	unsigned char* infi = new unsigned char[size];
	unsigned char* YUV444fi = new unsigned char[size];
	unsigned char* YUV420fi = new unsigned char[csize];
	unsigned char* YUV4442fi = new unsigned char[size];
	unsigned char* RGBfi = new unsigned char[size];

	infile.read((char*)infi, size);	
	rgb2yuv(infi, YUV444fi, size, usize, vsize);
	yuv444Tyuv420(YUV444fi,YUV420fi,size,weight);
	yuv420Tyuv444(YUV420fi, YUV4442fi, size, weight);
	yuv2rgb(YUV4442fi, RGBfi, usize, vsize);
	
	outYUV444.write((char*)YUV444fi, size);
	outYUV420.write((char*)YUV420fi, csize);
	outYUV4442.write((char*)YUV4442fi, size);
	outRGB.write((char*)RGBfi, csize);
	
	fileend(infi,YUV444fi,YUV420fi,RGBfi);

	infile.close();
	outYUV444.close();
	outYUV420.close();
	outRGB.close();

	return 0;
}

yuvrgb.h

#pragma once
void initLookupTable();
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);
void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize);
void fileend(unsigned char* infi, unsigned char* YUV444fi, unsigned char* YUV420fi, unsigned char* RGBfi);
void yuv444Tyuv420(unsigned char* yuv444,unsigned char* yuv420,int size,int weight);
void yuv420Tyuv444(unsigned char* YUV420, unsigned char* YUV444, int size, int weight);

yuvrgb.cpp

#pragma once
#include "yuvrgb.h"
#include <iostream>
using namespace std;

extern int* RGBYUV298;
extern int* RGBYUV411;
extern int* RGBYUV101;
extern int* RGBYUV211;
extern int* RGBYUV519;
extern int* RGBYUV66 ;
extern int* RGBYUV129;
extern int* RGBYUV25 ;
extern int* RGBYUV38 ;
extern int* RGBYUV74 ;
extern int* RGBYUV112;
extern int* RGBYUV94 ;
extern int* RGBYUV18 ;

void initLookupTable()
{
	for (int i = 0; i < 256; i++)
	{
		RGBYUV298[i] = 298 * i;
		RGBYUV411[i] = 411 * i;
		RGBYUV101[i] = 101 * i;
		RGBYUV211[i] = 211 * i;
		RGBYUV519[i] = 519 * i;
		RGBYUV66[i] = 66 * i;
		RGBYUV129[i] = 129 * i;
		RGBYUV25[i] = 25 * i;
		RGBYUV38[i] = 38 * i;
		RGBYUV74[i] = 74 * i;
		RGBYUV112[i] = 112 * i;
		RGBYUV94[i] = 94 * i;
		RGBYUV18[i] = 18 * i;
	}
}
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = int(*(yuv + i));
		u = int(*(yuv + i + usize));
		v = int(*(yuv + i + vsize));
		r = (RGBYUV298[y]+ RGBYUV411[v]-57344)>>8;
		if (r > 255) { r = 255; }
		if (r < 0) { r = 0; }

		g = (RGBYUV298[y] - RGBYUV101[u] - RGBYUV211[v] + 34739) >> 8;
		if (g > 255) { g = 255; }
		if (g < 0) { g = 0; }

		b = (RGBYUV298[y] + RGBYUV519[u] - 71117) >> 8;
		if (b > 255) { b = 255; }
		if (b < 0) { b = 0; }

		*(rgb + j) = unsigned char(b);
		*(rgb + j + 1) = unsigned char(g);
		*(rgb + j + 2) = unsigned char(r);
		j = j + 3;
	}
}

void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < size;)
	{
		b = int(*(rgb + i));
		g = int(*(rgb + i + 1));
		r = int(*(rgb + i + 2));
		y = ((RGBYUV66[r] + RGBYUV129[g] + RGBYUV25[b]) >> 8) + 16;
		u = ((-RGBYUV38[r] - RGBYUV74[g] + RGBYUV112[b]) >> 8) + 128;
		v = ((RGBYUV112[r] - RGBYUV94[g] - RGBYUV18[b]) >> 8) + 128;

		*(yuv + j) = unsigned char(y);
		*(yuv + j + usize) = unsigned char(u);
		*(yuv + j + vsize) = unsigned char(v);
		i = i + 3;//每個rgb爲1組
		j++;
	}
}

void yuv444Tyuv420(unsigned char* yuv444, unsigned char* yuv420,int size,int weight)
{
	int Ysize = size / 3;
	int j = 0;
	for (int i = 0; i < Ysize; i++)//Y分量的計算
	{
		*(yuv420 + j) = *(yuv444 + i);
		j++;
	}
	for (int i = 0; i < Ysize; )//U分量的計算
	{
		if (i % 2 == 1)
		{
			i++;
			continue;
		}
		if ((i) % (2 * weight) == 0)
		{
			i = i + weight;
			continue;
		}
		*(yuv420 + j) = *(yuv444 + i + Ysize);
		j++;
		i++;
	}
	for (int i = 0; i < Ysize; )//U分量的計算
	{
		if (i % 2 == 1)
		{
			i++;
			continue;
		}
		if ((i) % (2 * weight) == 0)
		{
			i = i + weight;
			continue;
		}
		*(yuv420 + j) = *(yuv444 + i + Ysize + Ysize);
		j++;
		i++;
	}
}
void yuv420Tyuv444(unsigned char* YUV420, unsigned char* YUV444, int size, int weight)
{
	int Ysize = size / 3;
	int j = 0;
	for (int i = 0; i < Ysize; i++)//Y分量的計算
	{
		*(YUV444 + i) = *(YUV420 + j);
		j++;
	}
	for (int i = 0; i < Ysize; )//U分量的計算
	{
		if (i % 2 == 1)
		{
			j = j - 1;
			*(YUV444 + i + Ysize) = *(YUV420 + j);
			i++;
			j++;
			continue;
		}
		if ((i) % (2 * weight) == 0)
		{
			j = j - weight/2;
			*(YUV444 + i + Ysize) = *(YUV420 + j);
			i++;
			j++;
			continue;
		}
		*(YUV444 + i + Ysize) = *(YUV420 + j);
		j++;
		i++;
	}
	for (int i = 0; i < Ysize; )//U分量的計算
	{
		if (i % 2 == 1)
		{
			j = j - 1;
			*(YUV444 + i + Ysize + Ysize) = *(YUV420 + j);
			i++;
			j++;
			continue;
		}
		if ((i) % (2 * weight) == 0)
		{
			j = j - weight/2;
			*(YUV444 + i + Ysize + Ysize) = *(YUV420 + j);
			i++;
			j++;
			continue;
		}
		*(YUV444 + i + Ysize + Ysize) = *(YUV420 + j);
		j++;
		i++;
	}
}
void fileend(unsigned char* infi, unsigned char* YUV444fi,unsigned char* YUV420fi, unsigned char* RGBfi)
{
	delete infi;
	delete YUV444fi;
	delete YUV420fi;
	delete RGBfi;
	delete	RGBYUV298;
	delete	RGBYUV411;
	delete	RGBYUV101;
	delete	RGBYUV211;
	delete	RGBYUV519;
	delete	RGBYUV66;
	delete	RGBYUV129;
	delete	RGBYUV25;
	delete	RGBYUV38;
	delete	RGBYUV74;
	delete	RGBYUV112;
	delete	RGBYUV94;
	delete	RGBYUV18;
}

實驗結果

  實驗結果有5幅圖,最開始的down.rgbdown.rgb,轉換成4:4:4的down444.yuvdown444.yuv,再轉換成4:2:0的down420.yuvdown420.yuv,再轉成4:4:4的down4442.yuvdown4442.yuv,再轉成cho.rgbcho.rgb。結果如下表:

down 444 420 4442 cho
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述

  至此,全部工作已完成。

(七)實驗誤差分析

  誤差來源可能是下述原因:

  1. RGBRGB文件和YUVYUV文件相互轉換的公式推導時,經過了多次量化和小數點的捨去,使得轉換公式本身便存在誤差。
  2. 在4:4:4取樣格式轉換爲4:2:0取樣格式時,捨去了較多的色差信號。
  3. 在4:2:0取樣格式轉換爲4:4:4取樣格式時,使用了同一點的像素值來代替缺失點的像素。
  4. 在文件轉換的過程中,產生了數據的溢出,並將溢出的點向上變爲0或者向下變爲255,也導致了誤差的出現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章