算法優化:rgb向yuv的轉化最優算法,ALPHABLEND算法優化快得讓你喫驚!

朋友曾經給我推薦了一個有關代碼優化的pdf文檔《讓你的軟件飛起來》,看完之後,感受頗深。爲了推廣其,同時也爲了自己加深印象,故將其總結爲word文檔。下面就是其的詳細內容總結,希望能於己於人都有所幫助。

 

速度取決於算法

同樣的事情,方法不一樣,效果也不一樣。比如,汽車引擎,可以讓你的速度超越馬車,卻無法超越音速;渦輪引擎,可以輕鬆 超越音障,卻無法飛出地球;如果有火箭發動機,就可以到達火星。

 

代碼的運算速度取決於以下幾個方面

1、  算法本身的複雜度,比如MPEG比JPEG複雜,JPEG比BMP圖片的編碼複雜。

2、  CPU自身的速度和設計架構

3、  CPU的總線帶寬

4、  您自己代碼的寫法

本文主要介紹如何優化您自己的code,實現軟件的加速。

 

先看看我的需求

我們一個圖象模式識別的項目,需要將RGB格式的彩色圖像先轉換成黑白圖像。

圖像轉換的公式如下:

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

    }

}

 

根據牛人的觀點,這種算法應該是非常快的了,以後可直接使用了。^_^

//#######################################################另一個人也是同樣的優化http://blog.csdn.net/housisong/article/details/1859084

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章