嗯,這個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, Format format ) 嗯,這個函數就是從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中將是補齊數據的數組,由此構造的圖像不會有任何問題。 好了,終於總結完了,有很多小的問題,我不想寫了,應該夠用了,如果有具體其他問題,大家再仔細想想,肯定能解決的,哈哈