rgb2yuv
1.rgb2yuv422p
?
代碼的運算速度取決于以下幾個方面
1、 算法本身的復雜度,比如MPEG比JPEG復雜,JPEG比BMP圖片的編碼復雜。
2、 CPU自身的速度和設計架構
3、 CPU的總線帶寬
4、 您自己代碼的寫法
將RGB格式的彩色圖像先轉換成YUV圖像。
圖像轉換的公式如下:
Y = 0.299 * R + 0.587 * G + 0.114 * B;
圖像尺寸640*480*24bit,RGB圖像已經按照RGBRGB順序排列的格式,放在內存里面了。
以下是輸入和輸出的定義:
#define XSIZE 640
#define YSIZE 480
#define IMGSIZE XSIZE * YSIZE
typedef struct RGB
{
unsigned char R;
unsigned char G;
unsigned char B;
}RGB;
struct RGB in[IMGSIZE]; //需要計算的原始數據
unsigned char out[IMGSIZE]; //計算后的結果
?
第一個優化
優化原則:圖像是一個2D數組,我用一個一維數組來存儲。編譯器處理一維數組的效率要高過二維數組。
?
先寫一個代碼:
Y = 0.299 * R + 0.587 * G + 0.114 * B;
void calc_lum()
{
int i;
for(i = 0; i < IMGSIZE; i++)
{
double r,g,b,y;
unsigned char yy;
r = in[i].r;
g = in[i].g;
b = in[i].b;
y = 0.299 * r + 0.587 * g + 0.114 * b;
yy = y;
out[i] = yy;
}
}
這大概是能想得出來的最簡單的寫法了,實在看不出有什么毛病,好了,編譯一下跑一跑吧。
第一次試跑
這個代碼分別用vc6.0和gcc編譯,生成2個版本,分別在pc上和我的embedded system上面跑。
速度多少?
在PC上,由于存在硬件浮點處理器,CPU頻率也夠高,計算速度為20秒。
我的embedded system,沒有以上2個優勢,浮點操作被編譯器分解成了整數運算,運算速度為120秒左右。
?
去掉浮點運算
上面這個代碼還沒有跑,我已經知道會很慢了,因為這其中有大量的浮點運算。只要能不用浮點運算,一定能快很多。
?
Y = 0.299 * R + 0.587 * G + 0.114 * B;
這個公式怎么能用定點的整數運算替代呢?
0.299 * R可以如何化簡?
Y = 0.299 * R + 0.587 * G + 0.114 * B;
Y = D + E + F;
D = 0.299 * R;
E = 0.587 * G;
F = 0.114 * B;
我們就先簡化算式D吧!
RGB的取值范圍都是0~255,都是整數,只是這個系數比較麻煩,不過這個系數可以表示為:0.299 = 299 / 1000;
所以 D = ( R * 299) / 1000;
Y = (R * 299 + G * 587 + B * 114) / 1000;
?
這一下,能快多少呢?
Embedded system上的速度為45秒;
PC上的速度為2秒;
0.299 * R可以如何化簡
Y = 0.299 * R + 0.587 * G + 0.114 * B;
Y = (R * 299 + G * 587 + B * 114) / 1000;
這個式子好像還有點復雜,可以再砍掉一個除法運算。
前面的算式D可以這樣寫:
0.299=299/1000=1224/4096
所以 D = (R * 1224) / 4096
Y=(R*1224)/4096+(G*2404)/4096+(B*467)/4096
再簡化為:
Y=(R*1224+G*2404+B*467)/4096
這里的/4096除法,因為它是2的N次方,所以可以用移位操作替代,往右移位12bit就是把某個數除以4096了。
?
void calc_lum()
{
int i;
for(i = 0; i < IMGSIZE; i++)
{
int r,g,b,y;
r = 1224 * in[i].r;
g = 2404 * in[i].g;
b = 467 * in[i].b;
y = r + g + b;
y = y >> 12; //這里去掉了除法運算
out[i] = y;
}
}
這個代碼編譯后,又快了20%。
雖然快了不少,還是太慢了一些,20秒處理一幅圖像,地球人都不能接受。
?
仔細端詳一下這個式子!
Y = 0.299 * R + 0.587 * G + 0.114 * B;
Y=D+E+F;
D=0.299*R;
E=0.587*G;
F=0.114*B;
?
RGB的取值有文章可做,RGB的取值永遠都大于等于0,小于等于255,我們能不能將D,E,F都預先計算好呢?然后用查表算法計算呢?
我們使用3個數組分別存放DEF的256種可能的取值,然后。。。
?
查表數組初始化
int D[256],F[256],E[256];
void table_init()
{
int i;
for(i=0;i<256;i++)
{
D[i]=i*1224;
D[i]=D[i]>>12;
E[i]=i*2404;
E[i]=E[i]>>12;
F[i]=i*467;
F[i]=F[i]>>12;
}
}
void calc_lum()
{
int i;
for(i = 0; i < IMGSIZE; i++)
{
int r,g,b,y;
r = D[in[i].r];//查表
g = E[in[i].g];
b = F[in[i].b];
y = r + g + b;
out[i] = y;
}
}
?
這一次的成績把我嚇出一身冷汗,執行時間居然從30秒一下提高到了2秒!在PC上測試這段代碼,眼皮還沒眨一下,代碼就執行完了。一下提高15倍,爽不爽?
繼續優化
很多embedded system的32bit CPU,都至少有2個ALU,能不能讓2個ALU都跑起來?
?
void calc_lum()
{
int i;
for(i = 0; i < IMGSIZE; i += 2) //一次并行處理2個數據
{
int r,g,b,y,r1,g1,b1,y1;
r = D[in[i].r];//查表 //這里給第一個ALU執行
g = E[in[i].g];
b = F[in[i].b];
y = r + g + b;
out[i] = y;
r1 = D[in[i + 1].r];//查表 //這里給第二個ALU執行
g1 = E[in[i + 1].g];
b1 = F[in[i + 1].b];
y = r1 + g1 + b1;
out[i + 1] = y;
}
}
2個ALU處理的數據不能有數據依賴,也就是說:某個ALU的輸入條件不能是別的ALU的輸出,這樣才可以并行。
這次成績是1秒。
?
查看這個代碼
int D[256],F[256],E[256]; //查表數組
void table_init()
{
int i;
for(i=0;i<256;i++)
{
D[i]=i*1224;
D[i]=D[i]>>12;
E[i]=i*2404;
E[i]=E[i]>>12;
F[i]=i*467;
F[i]=F[i]>>12;
}
}
到這里,似乎已經足夠快了,但是我們反復實驗,發現,還有辦法再快!
可以將int D[256],F[256],E[256]; //查表數組
更改為
unsigned short D[256],F[256],E[256]; //查表數組
?
這是因為編譯器處理int類型和處理unsigned short類型的效率不一樣。
再改動
inline void calc_lum()
{
int i;
for(i = 0; i < IMGSIZE; i += 2) //一次并行處理2個數據
{
int r,g,b,y,r1,g1,b1,y1;
r = D[in[i].r];//查表 //這里給第一個ALU執行
g = E[in[i].g];
b = F[in[i].b];
y = r + g + b;
out[i] = y;
r1 = D[in[i + 1].r];//查表 //這里給第二個ALU執行
g1 = E[in[i + 1].g];
b1 = F[in[i + 1].b];
y = r1 + g1 + b1;
out[i + 1] = y;
}
}
將函數聲明為inline,這樣編譯器就會將其嵌入到母函數中,可以減少CPU調用子函數所產生的開銷。
這次速度:0.5秒。
?
其實,我們還可以飛出地球的!
如果加上以下措施,應該還可以更快:
1、 把查表的數據放置在CPU的高速數據CACHE里面;
2、 把函數calc_lum()用匯編語言來寫
?
其實,CPU的潛力是很大的
1、 不要抱怨你的CPU,記住一句話:“只要功率足夠,磚頭都能飛!”
2、 同樣的需求,寫法不一樣,速度可以從120秒變化為0.5秒,說明CPU的潛能是很大的!看你如何去挖掘。
3、 我想:要是Microsoft的工程師都像我這樣優化代碼,我大概就可以用489跑windows XP了!
?
以上就是對《讓你的軟件飛起來》的摘錄,下面,我將按照這位牛人的介紹,對RGB到YCbCr的轉換算法做以總結。
?
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
?
?
#deinfe SIZE 256
#define XSIZE 640
#define YSIZE 480
#define IMGSIZE XSIZE * YSIZE
typedef struct RGB
{
unsigned char r;
unsigned char g;
unsigned char b;
}RGB;
struct RGB in[IMGSIZE]; //需要計算的原始數據
unsigned char out[IMGSIZE * 3]; //計算后的結果
?
unsigned short Y_R[SIZE],Y_G[SIZE],Y_B[SIZE],U_R[SIZE],U_G[SIZE],U_B[SIZE],V_R[SIZE],V_G[SIZE],V_B[SIZE]; //查表數組
void table_init()
{
int i;
for(i = 0; i < SIZE; i++)
{
Y_R[i] = (i * 1224) >> 12; //Y對應的查表數組
Y_G[i] = (i * 2404) >> 12;
Y_B[i] = (i * 467) >> 12;
U_R[i] = (i * 602) >> 12; //U對應的查表數組
U_G[i] = (i * 1183) >> 12;
U_B[i] = (i * 1785) >> 12;
V_R[i] = (i * 2519) >> 12; //V對應的查表數組
V_G[i] = (i * 2109) >> 12;
V_B[i] = (i * 409) >> 12;
}
}
?
inline void calc_lum()
{
int i;
for(i = 0; i < IMGSIZE; i += 2) //一次并行處理2個數據
{
out[i] = Y_R[in[i].r] + Y_G[in[i].g] + Y_B[in[i].b]; //Y
out[i + IMGSIZE] = U_B[in[i].b] - U_R[in[i].r] - U_G[in[i].g]; //U
out[i + 2 * IMGSIZE] = V_R[in[i].r] - V_G[in[i].g] - V_B[in[i].b]; //V
?
out[i + 1] = Y_R[in[i + 1].r] + Y_G[in[i + 1].g] + Y_B[in[i + 1].b]; //Y
out[i + 1 + IMGSIZE] = U_B[in[i + 1].b] - U_R[in[i + 1].r] - U_G[in[i + 1].g]; //U
out[i + 1 + 2 * IMGSIZE] = V_R[in[i + 1].r] - V_G[in[i + 1].g] - V_B[in[i + 1].b]; //V
}
}
?
從公式中,我們關鍵要理解的一點是,UV / CbCr信號實際上就是藍色差信號和紅色差信號,進而言之,實際上一定程度上間接的代表了藍色和紅色的強度,理解這一點對于我們理解各種顏色變換處理的過程會有很大的幫助。
??????? 我們在數字電子多媒體領域所談到的YUV格式,實際上準確的說,是以YcrCb色彩空間模型為基礎的具有多種存儲格式的一類顏色模型的家族(包括 YUV444 / YUV422 / YUV420 / YUV420P等等)。并不是傳統意義上用于PAL制模擬電視的YUV模型。這些YUV模型的區別主要在于UV數據的采樣方式和存儲方式,這里就不詳述。
??????? 而在Camera Sensor中,最常用的YUV模型是 YUV422格式,因為它采用4個字節描述兩個像素,能和RGB565模型比較好的兼容。有利于Camera Sensor和Camera controller的軟硬件接口設計。
?
http://blog.csdn.net/frankiewang008/article/details/6854616
?
?
http://hi.baidu.com/zymill/item/b09445aa563d02796cd4558b
http://wenku.baidu.com/link?url=j97XMVF-UMfdNgt8c4KM3J1lUe0beWdno23c10MJzS8AEImRtJTBo7ZHj2Zx6GAwwYNKAbN0pDNs_Nd_McNCaIK00NiQUTSZypxVHqfc5KO百度文庫位圖信息
總結
- 上一篇: 为什么Domain controller
- 下一篇: OpenCV学习(1) OpenCV的安