原地址:http://tracey2076.blog.51cto.com/1623739/539690
嗯,這個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中將是補齊數據的數組,由此構造的圖像不會有任何問題。
好了,終於總結完了,有很多小的問題,我不想寫了,應該夠用了,如果有具體其他問題,大家再仔細想想,肯定能解決的,哈哈