实验一:彩色空间转换(YUV2RGB)
#ifndef/#define/#endif?使用詳解:http://blog.csdn.net/abc5382334/article/details/18052757
Debug與Release版本的區別詳解:http://blog.csdn.net/ithzhang/article/details/7575483#comments
VS中Release和Debug模式的區別:http://blog.csdn.net/eric491179912/article/details/6154375
YUV格式分析:http://www.cnblogs.com/armlinux/archive/2012/02/15/2396763.html
一、實驗基本原理
1.????彩色空間轉換的基本思想及轉換公式
(1)???YUV與RGB空間的相互轉換
RGB格式轉YUV格式:
由電視原理知識,
Y = 0.2990 R + 0.5870 G + 0.1140 B
R-Y=0.7010 R - 0.5870 G - 0.1140 B
B-Y=-0.2990 R-0.5870 G + 0.8860 B
為了使色差信號的動態范圍控制在-0.5-0.5之間,需要進行歸一化,對色差信號引入壓縮系數,歸一化后的色差信號為:
U = - 0.1684 R - 0.3316 G + 0.5 B
V = 0.5 R - 0.4187 G - 0.0813 B
(2)???碼電平分配及數字表達式
亮電平信號:在對分量信號進行8比特均勻量化時,共分為256個等間隔的量化級。為了防止信號變動造成過載,在256級上端留20級,下端留16級作為信號超越動態范圍的保護帶。
色差信號:量化后碼電平分配色差信號經過歸一化處理后,動態范圍為-0.5-0.5,讓色差零電平對應碼電平128,色差信號總共占225個量化級。在256級上端留15級,下端留16級作為信號超越動態范圍的保護帶。
由此可知,YUV轉RGB公式為:
??????????????????????????R = Y + 1.4075 *(V-128)
G = Y– 0.3455 *(U–128) – 0.7169 *(V–128)
B = Y + 1.779 *(U–128)
(3)???色度格式
4:2:0格式:色差信號U,V的取樣頻率為亮度信號取樣頻率的四分之一,在水平方向和垂直方向上的取樣點數均為Y的一半。
所以:
RGB轉換YUV格式時,根據公式計算得到一幀Y、U、V數據后,要對色差信號U、V進行下采樣。
YUV轉換RGB格式時,在利用公式進行計算之前首先要對色差信號U、V進行上采樣。(下面是兩張丑丑的圖示~)
?
二、實驗流程
1.????建立工程,C相關知識回顧(養成良好代碼書寫習慣)
1)?????程序文件一般分為三部分:
(1)頭文件:包括與結構(類)的聲明和使用這些結構(類)的函數的原型,不要將函數的定義或變量的聲明放在頭文件中,#ifndef/#define/#endif?防止頭文件被多次引用,詳解見文章頭部鏈接。
(2)源代碼文件:包含與結構(類)有關的函數的代碼,即函數的定義
(3)源代碼文件(main.c/cpp):包含調用這些函數的代碼
2)?????學會使用帶參主函數定義以及命令參數、可執行文件工作路徑的設置
VS2013步驟為:項目→項目屬性→配置屬性→調試→命令參數(編輯)/工作目錄(編輯:絕對路徑/瀏覽:找到具體文件夾目錄)如下圖:
??????
??????? 關于參數設置過程中Debug與Release版本的區別詳解鏈接見文章頭部鏈接。
2.????調試RGB轉換YUV程序
理解課上所給例程,尤其對色差信號進行4:2:0下采樣部分,妙用指針可以很容易實現,在課程作業編寫YUV轉換RGB程序中對U、V數據進行上采樣也是借鑒了例程的指針用法。
3.????編寫YUV轉換RGB程序,具體見下一部分。
三、關鍵代碼
#include "stdlib.h" #include "yuv2rgb.h" /*YUV->RGB 轉換公式 R = Y + 1.4075 *(V-128) G = Y – 0.3455 *(U –128) – 0.7169 *(V –128) B = Y + 1.779 *(U – 128) */ static float YUV2RGB14075[256]; static float YUV2RGB03455[256], YUV2RGB07169[256]; static float YUV2RGB1779[256];/************************************************************************ * * int YUV2RGB (int x_dim, int y_dim, void *ychunk, void *uchunk, void *vchunk, void *rgb_out, int flip) * * Purpose : It takes a YUV (4:2:0) format and convert it into * a 24-bit RGB bitmap. * * Input : x_dim the x dimension of the bitmap * y_dim the y dimension of the bitmap* * yuv chunk pointer to the YUV structurergb_out pointer to the buffer of the bitmap * * Output : 0 OK * 1 wrong dimension * 2 memory allocation error * * Side Effect : * None * * Date : 03/11/2017 * ************************************************************************/int YUV2RGB(int x_dim, int y_dim, void *ychunk, void *uchunk, void *vchunk, void *rgb_out, int flip) { static int init_done = 0;long i, j, size;unsigned char *r, *g, *b;unsigned char *y, *u, *v;unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv; unsigned char *addu_buffer, *addv_buffer;unsigned char *addu_buf, *addv_buf;if (init_done == 0){InitLookupTable();init_done = 1;}// check to see if x_dim and y_dim are divisible by 2if ((x_dim % 2) || (y_dim % 2)) return 1;size = x_dim * y_dim;// allocate memory addu_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));addv_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));b = (unsigned char *)rgb_out;y = (unsigned char *)ychunk; u = (unsigned char*)uchunk;v = (unsigned char*)vchunk;addu_buf = (unsigned char *)addu_buffer;addv_buf = (unsigned char *)addv_buffer;// 對UV塊進行上采樣for (j = 0; j < y_dim/2; j++){psu = u + j * x_dim / 2;//原始數據U塊指針psv = v + j * x_dim / 2;//原始數據V塊指針pu1 = addu_buf + 2 * j * x_dim; //U塊奇數行首地址pu2 = addu_buf + (2 * j + 1) * x_dim;//U塊偶數行首地址pv1 = addv_buf + 2 * j * x_dim; //V塊奇數行首地址pv2 = addv_buf + (2 * j + 1) * x_dim;//V塊偶數行首地址for (i = 0; i < x_dim/2; i++){*pu1 = *psu;*pu2 = *psu;pu1++;pu2++;*pu1 = *psu;*pu2 = *psu; //U塊上采樣:“以1作4”*pv1 = *psv;*pv2 = *psv; pv1++;pv2++; *pv1 = *psv;*pv2 = *psv; //V塊上采樣:“以1作4”pu1++;pu2++;pv1++;pv2++;psu++;psv++; }} // convert YUV to RGBif (!flip) {for (j = 0; j < y_dim; j++){y = y + (y_dim - j - 1) * x_dim;addu_buf = addu_buf + (y_dim - j - 1) * x_dim;addv_buf = addv_buf + (y_dim - j - 1) * x_dim;for (i = 0; i < x_dim; i++) {g = b + 1;r = b + 2;adjust(b, g, r, y, addu_buf, addv_buf);b += 3;y++;addu_buf++;addv_buf++;}}}else { for (i = 0; i < size; i++){g = b + 1;r = b + 2;adjust(b, g, r, y, addu_buf, addv_buf);//對變量范圍作適應性調整b += 3;y++;addu_buf++;addv_buf++;}}free(addu_buffer);free(addv_buffer);return 0; } void InitLookupTable() {int i;for (i = 16; i < 240; i++) YUV2RGB14075[i] = (float)1.4075 * (i-128);for (i = 16; i < 240; i++) YUV2RGB03455[i] = (float)0.3455 * (i - 128);for (i = 16; i < 240; i++) YUV2RGB07169[i] = (float)0.7169 * (i - 128);//for (i = 16; i < 240; i++) YUV2RGB1779[i] = (float)1.779 * (i-128); } void adjust(unsigned char *b, unsigned char *g, unsigned char *r, unsigned char *y, unsigned char *u, unsigned char *v) {float temp = 0;temp = (float)(*y + YUV2RGB1779[*u]);temp = temp>255 ? 255 : temp;temp = temp<0 ? 0 : temp;*b = (unsigned char)temp;temp = (float)(*y - YUV2RGB03455[*u] - YUV2RGB07169[*v]);temp = temp>255 ? 255 : temp;temp = temp<0 ? 0 : temp;*g = (unsigned char)temp;temp = (float)(*y + YUV2RGB14075[*v]);temp = temp>255 ? 255 : temp;temp = temp<0 ? 0 : temp;*r = (unsigned char)temp; }
四、程序分析
①RGB格式裸數據部分,三通道的排列順序依次是B\G\R,所以一個像素點有三個分別表示顏色的數據。
②?YUV格式通常有兩大類:
打包(packed)格式:將YUV分量存放在同一個數組中,通常是幾個相鄰的像素組成一個宏像素(macro-pixel);
平面(planar)格式:使用三個數組分開存放YUV三個分量,就像是一個三維平面一樣。
下面是我們測試所用RGB與YUV文件的兩張丑丑的排列簡示圖~
依次是B G R B G R.........
每幀數據Y U V分塊排列......
程序實現過程:
1.? 主程序實現對YUV視頻逐幀讀取數據分別處理,根據格式特點,開辟三個緩存分別存放YUV三個分量各自的數據。
2.? 關鍵函數首先對U\V色差信號作上采樣,下采樣過程中相鄰四個數據取平均得到一個U/V,所以上采樣我們這里相應地就可以->以1代4。
3.? 接著就是帶入公式計算,依然使用查找表的方法,重點注意強制類型轉換過程,在void adjust(~~~)函數中實現,超出0~255范圍內的rgb值, 負數取0,大于255的取255。
4.? 最后按照存儲格式寫入文件。
五、實驗結果
(所有YUV測試文件均來自網站:http://trace.eas.asu.edu/yuv/index.html)
1.? 如何檢驗程序結果是否正確:
由于RGB的裸數據并不構成文件格式,所以我們的YUV視頻通過程序轉換成RGB文件以后,再用RGB2YUV程序轉換成YUV格式文件,用YUV播放 器查看,對比前后兩個視頻圖像畫面,若有明顯色調失真或不明色塊之類則表明程序出錯。
2.? 下面是這次實驗程序編寫過程中出現的錯誤整理:
①程序結果出錯:
測試得到上圖結果時,首先注意到明顯的藍紅色塊,猜測是公式計算過程中強制類型轉換錯誤,老師上課也提醒過,查閱相關書籍得到C語言各種數據類型大小范圍如下圖:
改正: 將公式計算和強制類型轉換過程另寫函數,將計算所得結果在unsignedchar類型 0~255范圍之外的再次處理。
②程序運行報錯:program received signal SIGSEGV,Segmentationfault.
通過相關搜索以及不斷改正調試發現,出現這種錯誤的原因是因為聲明指針后沒有初始化,指針指向地址為未知,未初始化指針直接執行運算就會出錯。
改正:程序中聲明的指針初始化。
③程序本身錯誤:對比差錯圖像與原圖像具體差異:
??????? 下面是一組結果圖:
藍紅色塊錯誤處理完之后,得到的圖像還是不對,但對于錯誤原因不是很清楚,于是再次進行各種測試。
看上去好像是色調改變了
繼續測試
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
還好找到了這個顏色豐富亮麗的YUV,幫了大忙
似曾相識的豎條色彩分界
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
所以這應該是發生了某部分數據的位置偏移,問題應該出現在有關數據計算部分。
??????? 于是重新查看程序,在直接得到數據的計算過程和U/V色差信號的上采樣兩部分仔細排查,最終發現錯誤出現采樣程序部分的指針使用。
??????? 錯誤:程序中指針psu\psv多進行了一次偏移超出數據范圍。
上采樣部分正確代碼已經在上面程序部分給出,指針使用圖示如下:
指針簡圖(V同理):
以上錯誤改正以后再次進行多次測試,結果表明程序運行結果正確。
test 1----
test 2----
test 3----
五、結論
掌握指針運算很重要,會使程序變的簡單。
tips/2017/04/30:對于人眼來說,亮度信號是最敏感的,如果將彩色圖像轉換為灰度圖像,僅僅需要轉換保存亮度信號就可以。
?
YUV官方播放器的注冊方法:
官方提供注冊碼:
REGEDIT4
?
[HKEY_CURRENT_USER\Software\tihohod.com\YUVPlayer\RegistrationInfo]
"RegistrationName"="Peace on Earth -Goodwill to Men"
"RegistrationKey"="JxsQHEseR43VXvp6kCV8w5ACtqNVJ11LhOAE4Pztk+Ksw211wohNjsR78XHoGajwlqIOU6gVz0zipwkJCfdvIqyAJrEgJLYB+NKwPGaD+OqbJD+oe+5xixyIKc7tT0ecQjNER47sA3HsUUhRl3LmKozxS2nH233WNPpJwoF4rL1Bjm5OfcM/jiixF/By85wQTdzSwAehjfvB7iO9tCaI9A=="
把上面key的內容復制到一個編輯器,另存為key.reg,點擊運行這個文件會自動注冊,就OK了
總結
以上是生活随笔為你收集整理的实验一:彩色空间转换(YUV2RGB)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [算法入门笔记] 18. 动态规划
- 下一篇: docker及k8s容器面试精华汇总(一