項目中用到大量基礎影像處理知識,其中灰階圖的產生是很重要的一環。
先補充一些基礎知識:
----------------------------------------------------------------------------------------------------------------------------
一:灰階圖
灰階圖就是黑白圖,整幅圖片只有不同程度的黑白兩色。灰階也可認為是亮度,簡單的說就是色彩的深淺程度 !
1:如果我們用八位來儲存灰階圖。則共有256種組合。那相當於:我們把從:純黑 到 純白 之間區分成了256種灰階。從而對應256種灰階值! 而如果用一個數字來表示的話:則0-255每個數字對應一種灰階!
2:灰階就是沒有色彩,它的RGB色彩分量全部相等。 比如 rgb(20,20,20)。 既然這樣:我們完全可以用rgb中的某一個分量的值 來代替這個實際的灰階值! 比如上邊這個例子:我們完全可以用20來代替這個灰階! 這是一個一一映射關係!
--------------------------------------------------------------------------------------------------------------
二:色彩表
色彩表就是表示當前所有顏色的一張表。 而我們知道任何一種顏色都可以用rgb值來表示。如此我們完全可以設計一張表,裡邊每個元素都是一個rgb值,從而將所有的顏色都用rgb值表示出來!
--------------------------------------------------------------------------------------------------------------
三:色彩索引模式
在一張圖片的每個像素中可以直接存放其rgb值!當然:我們也可以存放一個索引值,通過這個索引值去其對應的顏色表中去尋找對應的顏色的rgb值來進行繪製。這種像素中存放索引值而不是實際rgb值的模式在Qt中有:QImage::Format_Indexed8,亦即:用8位來存放一個索引值。
--------------------------------------------------------------------------------------------------------------
四:灰階索引圖:
對於8階灰階圖而言:由於其一共有256種灰階,所以我們可以設計一個顏色表:裡邊存放256個rgb值!每個rgb值的三個分量都是相等的。這樣:這張表就可以用來表示所有的灰階!
灰階索引圖中存放的是各個整數索引值,這些圖片資料本身是無法顯示的,因為其沒有實際的rgb值。當實際構圖時:是用該索引值在上邊建立的顏色表中進行尋找,找到對應的灰階rgb值,而後才能進行實際的繪圖等操作。
有了上兩步基礎,問題的關鍵就在於:這個灰階索引值是如何獲得的?
==================================================================================================================
有了上述基礎知識,下一步可以來看一下如何從彩色圖轉為灰階圖了。
一:整體流程:
1:依據某一個演算法:將rgb32彩色映像的每個像素中的rgb值轉為一個整數灰階值。
2:用這個計算獲得的值來替代原圖中的每個rgb。
這樣就可以將一張彩色圖轉為灰階圖了。
但是這個過程中存在以下問題:
1:如何從彩色圖原本的rgb值計算出灰階值?
2:因為每個像素的rgb都被灰階值取代,其都是一樣的,這完全可以用一個灰階值取代,而不是用三個。用三個有點浪費空間。
--------------------------------------------------------------------------------------------------------------
對於問題1:灰階值的計算方法有以下幾種:
1.浮點演算法:Gray=R*0.3+G*0.59+B*0.11
2.整數方法:Gray=(R*30+G*59++B*11)/100
3.移位方法:Gray =(R*28+G*151+B*77)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.僅取綠色:Gray=G;
而各種方法之間的區別在於:精確度! 亦即:得到的灰階圖的效果不同!浮點數運算得到的效果最好,而僅僅取綠色得到的效果最差。至於取哪一種,則取決於實際需要!
--------------------------------------------------------------------------------------------------------------
對於問題2:我們可以每個像素中只存唯一一個灰階值,而後設計一個色彩表,通過這個灰階值去對應尋找其真實的顏色。那這樣就可以 用到上邊所的灰階索引圖了。亦即:此時圖中存放的不是真實的rgb值,而是一個灰階所以值;而後設計一張其對應的顏色 表。這樣二者便可以完全代表整張灰階圖了。而且二者加起來的空間也比非索引模式要小的多,而且尋找速度會更快。
所以:最好是將灰階圖用索引模式進行儲存,這在Qt中是指定QImage格式為Index8實現。亦即:有8位索引,可以指定256種灰階值。
那麼:我們從彩色圖轉為灰階索引圖的整體流程為:
(以rgb32轉為index8為例)
1:首先我們要建立一張空的index8映像。
2:依據某一個演算法:將rgb32彩色映像的每個像素中的rgb值轉為一個整數值,儲存在index8灰階圖的每個像素中。
3:構建顏色表。
4:依據儲存在index映像中的整數值,去灰階表中尋找對應的灰階來進行實際的構圖!
==================================================================================================================
OK,現在方法有了,下邊我們可以來實現了。
1:最簡單的方法:
[cpp] view plaincopy
- for(int i =0;i<width;i++)
- {
- for(int j=0;j<height;j++)
- {
- QRgb pixel=iImage->pixel(i,j);
- int r=qRed(pixel);
- int g=qGreen(pixel);
- int b=qBlue(pixel);
- iGray->setPixel(i,j,qGray(r,g,b));
- }
- }
亦即:先用 pixel()擷取每個像素點的QRgb值。而後分別用qRed(),qGreen(),qBlue()來擷取rgb分量的值。而後進行上述灰階值的計算。之後再重新賦予給該像素點。
但是實際運行一下你會發現:對於600*800大小的映像:這個函數要運行30s+,這顯然是不能容忍的。
--------------------------------------------------------------------------------------------------------------
上邊這個方法很簡單直接,但是效率上太失敗,所以我們需要來最佳化效率,主要從以下兩個方面實現:
1:要用pixel() ,qRed(),qGreen(),qBlue()來遍曆擷取每個像素的rgb分量。其函數實現時:肯定是一個尋找過程,尤其是依據像素位置來獲得rgb值的函數pixel()。每次都一個完整的尋找,這顯然效率較為低下,尤其是不斷的大量尋找! 由於資料都是連續存放在記憶體中的,我們完全可以通過移動指標的方式來擷取rgb分量。每次只移動一個指標位置就可以了。這種移動指標的方式顯然比全尋找要快!
這塊代碼實現上:其是直接來取用green綠色分量值 來作為gray索引值!
其比上一個方法的最佳化之處在於:它沒有使用系統函數,而是使用移動指標的方式來取得rgb分量。它把所有灰階索引值存放到index8灰階圖中。最後建立了8階灰階圖的顏色表並賦予給該灰階圖。這樣:系統會自動(不需要我們手動去尋找)依據儲存在映像中的灰階索引值去 顏色表中尋找對應灰階的rgb值來進行繪製!
--------------------------------------------------------------------------------------------------------------
2:計算灰階索引值的函數qGray(),按照Qt文檔上所說:其計算過程為:(r*11+g*16+5*5)/32 .乘除法是一個很大的計算量,最好用移位操作來進行取代!
(這就牽扯一個問題:原本我以為:乘除法在寄存器中就算轉為移位操作的,所以乘除法和移位操作在效率上應該沒什麼區別。但是實際不是這樣的,因為:如果你使用乘除法,則其在運行時:轉化成的乘法指令本身會消耗很多cpu周期,所以這無形中已經降低了效率!所以來說:在做高效能運算時:最好用移位操作來取代乘除法)
============================================================================================
知道如何最佳化後,我們看下在Qt中如何?,這個在網上我找到了一個方法,摘錄如下
[cpp] view plaincopy
- QImage colourImg("colourImage.bmp");
- QSize colourImgSize = colourImg.size();
- int width = colourImgSize.rwidth();
- int height = colourImgSize.rheight();
- unsigned char *colourImgDataPtr = colourImg.bits();
- QImage grayImg(colourImgSize, QImage::Format_Indexed8);
- unsigned char *grayImgDataPtr = grayImg.bits();
-
- //下邊這個for迴圈是直接取用green綠色分量值 來作為gray索引值
- for(int i = 0; i < height; i++)
- {
- for(int j = 0; j < width; j++)
- {
- *grayImgDataPtr = *(colourImgDataPtr + 1); colourImgDataPtr += 4; grayImgDataPtr++;
- }
- }
-
- QVector<QRgb> grayColourTable;
- unsigned int rgb = 0;
- for(int i = 0; i < 256; i++)
- {
- grayColourTable.append(rgb);
- rgb += 0x00010101;
- }
-
- grayImg.setColourTable(rayColourTable);
- graImg.save("grayImage.bmp", "bmp");
這塊代碼實現上:其是直接來取用green綠色分量值 來作為gray索引值!
其比上一個方法的最佳化之處在於:它沒有使用系統函數,而是使用移動指標的方式來取得rgb分量。它把所有灰階索引值存放到index8灰階圖中。最後建立了8階灰階圖的顏色表並賦予給該灰階圖。這樣:系統會自動(不需要我們手動去尋找)依據儲存在映像中的灰階索引值去 顏色表中尋找對應灰階的rgb值來進行繪製!
--------------------------------------------------------------------------------------------------------------
在上述方法的基礎上,我們在使用時:需要注意的是效果能不能達到需求,從而可能要用精確度更高的灰階索引值計算方法。那這就牽扯大量運算,此時記得要用移位操作 來取代 乘除法運算
[cpp] view plaincopy
- QBYTE RGBtoGRAY(QBYTE r, QBYTE g, QBYTE b)
- {
- return (QBYTE)((((QUINT32)((r << 5) + (r << 2) + (r << 1)))+ (QUINT32)((g << 6) + (g << 3) + (g << 1) + g)
- + (QUINT32)((b << 4) - b)) >> 7);
- }
這個是網上找到的最佳化的Realtime Compute灰階索引值演算法。沒有用到乘除法運算,轉而使用大量移位操作。
--------------------------------------------------------------------------------------------------------------
暫且總結到這裡,上述代碼都實際測試過,沒有問題。