QImage 圖像格式小結

嗯,這個QImage的問題研究好久了,有段時間沒用,忘了,已經被兩次問到了,突然有點解釋不清楚,我汗顏,覺得有必要重新總結下了,不然無顏對自己了。

圖像的數據是以字節爲單位保存的,每一行的字節數必須是4的整數倍,不足的補0。

(因爲我們使用的是32操作系統,因此數據是按照32位對齊的,所以每行的字節數必須是4的整數倍也就是說每行的數據位必須是32位的整數倍。)這裏是按照我的理解的,貌似錯了,修正一下,最近在看數據對齊,這段話先忽略了,沒有刪掉,是因爲,想留個足跡,等我找到合適的答案再貼上來。不過,圖像的數據確實是按32位對齊的。

如果不是整數倍,則根據公式: W = ( w * bitcount + 31 )/32 * 4;

注:  w是圖像的寬度,bitcount是圖像的位深,即32、24等, 計算得到的W是程序中圖像每行的字節數。

這裏講述QImage的32、24、8位圖。

圖像格式:QImage::Format_RGB32 ,QImage::Format_RGB888,QImage::Format_Indexed8。

構造圖像:

    (1)、QImage myImage1 = QImage(filename);  根據文件名打開圖像,如果圖像本身是32、24位的,程序中圖像是32位的,如果圖像本身是8位、1位的,程序中對應爲8位、1位。

   (2)、QImage myImage2 = QImage(width, height, QImage::Format_…); 根據圖像寬高來構造一幅圖像,程序會自動根據圖像格式對齊圖像數據。

操作圖像:按照(2)的方式構造圖像,在Debug下,如果不給圖像myImage2初值,圖像不是黑的, 但release下,則構造好的圖像默認爲黑色。

 

好了,現在我們需要對圖像數據操作,32位圖像無疑是最簡單的,因爲它數據是對齊的。用width表示圖像寬度,height表示圖像高度。

首先熟悉幾個函數:

a、uchar* bits();       可以獲取圖像的首地址

b、int  byteCount();  圖像的總字節數 

c、int  bytesPerLine(); 圖像每行字節數

1、QImage::Format_RGB32,存入格式爲B,G,R,A 對應 0,1,2,3

    QImage::Format_RGB888,存入格式爲R, G, B 對應 0,1,2

    QImage::Format_Indexed8,需要設定顏色表,QVector<QRgb>

    灰度圖像顏色表設定:

    QVector<QRgb>  colorTable;

    for(int k=0;k<256;++k) 
    {

           colorTable.push_back( qRgb(k,k,k) );

    }

2、QImage  image32 = QImage(width, height, QImage::Format_32);

     QImage  image24 = QImage(width, height, QImage::Format_24);

     QImage  image8 = QImage(width, height, QImage::Format_8);

     image8.setColorTable(colorTable);

3、需要取每個像素處理,採用指針取值,行掃的方式:

    int  lineNum_32 = 0;     //行數

    int  pixelsub_32 = 0;    //像素下標

    uchar*    imagebits_32 = image32.bits();         //獲取圖像首地址,32位圖

    uchar*    imagebits24 = image24.bits();

    uchar*    imagebits8 = image8.bits();

    for(int i=0; i<height; ++i)

    {   

           //按照通常的理解,我們會如下處理,取每行

           lineNum_32 = i * width * 4;                  //對於任意圖像,這句沒有問題

           // lineNum_24 = i * width * 3;             //??當width不是4的整數倍時,這句取不到每行開頭

           // lineNum_8 = i * width;                 //??當width不是4的整數倍時,這句取不到每行開頭

         for(int j=0; j<width; ++j)

         {

               int r_32 = imagebits_32[ lineNum_32 + j * 4 + 2]; 

               int g_32 = imagebits_32[ lineNum_32 + j * 4 + 1];

               int b_32 = imagebits_32[ lineNum _32 + j * 4];

 

               // int r_24 = imagebits_24[ lineNum_24 + j * 3];        //注意區別32位的圖

               // int g_24 = imagebits_24[ lineNum_24 + j *3 + 1];

               // int b_24 = imagebits_24[ lineNum_24 + j * 3 + 2];

               // int gray_8 = imagebits_8[ lineNum_8 + j];

               ……

               //自己的操作

         }

    }

//??出問題了,因爲實際的圖像數據並不是以width爲真實寬度的,解決,有兩種方法:

 

第一種方法:自己計算實際的寬度

修改爲:

// 獲取每行的字節數

int  W_32 = ( width * 32 + 31 )/32 * 4;           //注意這裏沒有四捨五入,所以不要隨意換算

int  W_24 = ( width * 24 + 31 )/32 * 4;

int  W_8 = ( width * 8 + 31)/32 * 4;

       //也可以使用QT函數來獲取,功能和上面一樣

{

        int  W_32 = image32.bytesPerLine();

        int  W_24 = image24.bytesPerLine();

        int  W_8 = image8.bytesPerLine();

}

for(int i=0;  i<height;  ++i)

{   

           //現在可以按照通常的理解,取每行

           lineNum_32 = i * W_32;               //注意,這裏不再需要乘倍數了(4, 3等)

           // lineNum_24 = i * W_24;            

           // lineNum_8 = i * W_8;                   

         for(int j=0; j<width; ++j)

         {

               //這裏的操作同上面的一樣

         }

}

 

第二種方法:採用scanLine(int)來獲取每行的首地址,

for(int i=0; i<height; ++i)

{   

       imagebits_32 = image32.scanLine(i);

       imagebits_24 = image24.scanLine(i);

       imagebits_8 = image8.scanLine(i);

       for(int j=0; j<width; ++j)

      {

               int r_32 = imagebits_32[ j * 4 + 2]; 

               int g_32 = imagebits_32[ j * 4 + 1];

               int b_32 = imagebits_32[ j * 4];

 

               // int r_24 = imagebits_24[ j * 3];       

               // int g_24 = imagebits_24[ j *3 + 1];

               // int b_24 = imagebits_24[ j * 3 + 2];

               // int gray_8 = imagebits_8[ j ];

               ……

               //自己的操作

      }

}

OK,上述兩種方法的索引就不會出現圖像數據偏移的問題

4、大家注意到QImage的這個構造函數了吧,QImage::QImage ( uchar * data, int width, int height, Formatformat )

     嗯,這個函數就是從uchar* 的數據來構造圖像,一般我們都可能先將圖像數據存在uchar*中,

     uchar* data32 = new uchar[ width * height * 4];      

     uchar* data24 = new uchar[ width * height * 3];

     uchar* data8 = new uchar[ width * height];

     從data32構造圖像,不會有任何問題,但是當width不是4的整數倍時,你就不可能從data24和data8構造出自己想要的數據,程序會掛掉的,因爲這兩個數組的數據量根本不夠一幅圖(還記得數據補齊不)。

解決辦法:

你需要首先計算出,你的圖像的真實數據量(字節數), 可以根據QImage.byteCount()函數來獲取圖像的字節數,當然,你也可以自己計算,計算公式 byteCount = height * W;   這裏的W就是每行的字節數,上面已經講過了它的計算方法。

然後,你可以由QByteArray來獲取轉換的指針數據:

如:你的圖像數據放在數組  uchar*  srcData;  中

QByteArray imageByteArray = QByteArray( (const char*)srcData,  byteCount );

uchar*  transData = (unsigned char*)imageByteArray.data();

QImage desImage = QImage(transData, width, height, QImage::Format_…);

嗯,經過上述轉換後,transData中將是補齊數據的數組,由此構造的圖像不會有任何問題。

 

好了,終於總結完了,有很多小的問題,我不想寫了,應該夠用了,如果有具體其他問題,大家再仔細想想,肯定能解決的,哈哈


轉載 :http://tracey2076.blog.51cto.com/1623739/539690

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