Qt中QImage用于16位图像的显示,QImage数据对齐
Qt中QImage用于16位圖像的顯示,QImage數據對齊
之前總結過QImage類,https://blog.csdn.net/weixin_43294620/article/details/122419099?spm=1001.2014.3001.5501
但是對于16位圖像的顯示沒有詳細說明,Qt也支持16位的圖像,例如QImage::Format_Grayscale16、QImage::Format_RGBX64,以上兩種格式分別是Qt5.13和5.12版本引入的,現在分別對以上兩種圖像進行分析,為了圖像轉換方便,在分析的時候同時引入QImage::Format_Indexed8格式,圖像分析的時候統一用一個height=10, width=9的圖像進行分析,width設為9是為了說明QImage數據對齊的問題。
一、Format_Indexed8格式
QImage qimg8(9,10,QImage::Format_Indexed8); //新建一個10行9列的QImage數組,會自動數據對齊,每行12字節 qimg8.setColorCount(256); // grayscale 256 for (int i = 0; i < 256; i++) {qimg8.setColor(i, qRgb(i, i, i)); } for(int y =0;y<10;y++) {for(int x=0;x<9;x++){qimg8.setPixel(x,y,(y+1)*10+x+1); //Note: (x,y) means coordinate,不是行列下標!!!!} } //為了顯示方便,最后一行設為255 for(int final=0; final<9; final++) {qimg8.setPixel(final,9,255); } qDebug() << u8"Total bytes of Image: " << qimg8.sizeInBytes() << Qt::endl; //'byteCount()' is deprecated: Use the sizeInBytes. //Deprecated means this method is obsolete and temporarily available, but it will not be updated in the future. //It may be deleted later. It is recommended that later people not call this method.qDebug() << u8"Bytes per Line of Image: " << qimg8.bytesPerLine() << Qt::endl; //uchar * pUchar = qimg8.scanLine(0); qDebug() << u8"Address of scanLine(0): " << pUchar << Qt::endl; qDebug() << u8"Value of scanLine(0): " << *pUchar << Qt::endl; for(int y = 0;y<10;y++) //print the value of each point { qDebug() << "Value of " << y+1 << "Rows: " << qimg8.pixelIndex(0,y) << qimg8.pixelIndex(1,y)<<qimg8.pixelIndex(2,y) <<qimg8.pixelIndex(3,y) <<qimg8.pixelIndex(4,y)<<qimg8.pixelIndex(5,y) <<qimg8.pixelIndex(6,y) <<qimg8.pixelIndex(7,y)<<qimg8.pixelIndex(8,y) << Qt::endl; }以上輸出結果為:
Value of 1 Rows: 11 12 13 14 15 16 17 18 19 Value of 2 Rows: 21 22 23 24 25 26 27 28 29 Value of 3 Rows: 31 32 33 34 35 36 37 38 39 Value of 4 Rows: 41 42 43 44 45 46 47 48 49 Value of 5 Rows: 51 52 53 54 55 56 57 58 59 Value of 6 Rows: 61 62 63 64 65 66 67 68 69 Value of 7 Rows: 71 72 73 74 75 76 77 78 79 Value of 8 Rows: 81 82 83 84 85 86 87 88 89 Value of 9 Rows: 91 92 93 94 95 96 97 98 99 Value of 10 Rows: 255 255 255 255 255 255 255 255 255圖像顯示如下:
如果在Debug模式下可以看到Qt中圖像的信息為:
Format_Indexed8并不能顯示16位圖像,在此先借用格式說明一下QImage的數據對齊,后面還會用到此類型的圖像進行格式轉換。我們創建的是9*10的QImage,但實際上每行有12字節,而不是9字節。這是因為QImage是32位對齊的(應該是為了執行效率),也可以說4字節對齊??梢愿鶕酱_定W=(width×bitcount+31)/32×4W=(width×bitcount+31)/32×4W=(width×bitcount+31)/32×4,其中width為圖像的實際寬度,bitcount是圖像的位深,例如以上圖像的位深為8,W是計算得到的程序中圖像每行的字節數。所以創建9*10的圖像時,實際的寬度W=(9×8+31)/8=12W=(9×8+31)/8=12W=(9×8+31)/8=12
但是注意雖然實際存儲的時候可能會比原圖像寬度大,但是顯示并不會顯示多出來的部分,例如以上圖像,利用指針打印出前12個像素值:
uchar * pUchar = qimg8.scanLine(0); for(int i = 0;i<12;i++) //print the value of each point through pointer! {qDebug() << *(pUchar+i) ; }結果如下:11 12 13 14 15 16 17 18 19 205 205 205
可以看到,補充的值為205,但并沒有顯示該部分(豎白條),雖然沒有顯示,但是實際上在內存中是有隨機值的,所以在操作像素的時候應注意對齊的情況,最好是采用scanLine()獲得每一行的首指針,然后操作,避免一直用scanLine(0)的情況。
二、Format_Indexed8直接轉換成Format_RGBX64
QImage img8to16 = qimg8.convertToFormat(QImage::Format_RGBX64); //QImage img8to16 = qimg8.convertTo(QImage::Format_RGBX64); //void QImage::convertTo(QImage::Format format, Qt::ImageConversionFlags flags = Qt::AutoColor) *qimgHi = img8to16;qDebug() << u8"Total bytes of Image: " << img8to16.sizeInBytes() << Qt::endl; qDebug() << u8"Bytes per Line of Image: " << img8to16.bytesPerLine() << Qt::endl; uchar * pGrayscale16 = img8to16.scanLine(0);qDebug() << u8"Value of scanLine(0): " << *pGrayscale16 << Qt::endl; qDebug() << u8"Value of qAlpha(0): " << qAlpha(img8to16.pixel(0,0)) << Qt::endl; qDebug() << u8"Value of qRed(0): " << qRed(img8to16.pixel(0,0)) << Qt::endl; qDebug() << u8"Value of qGreen(0): " << qGreen(img8to16.pixel(0,0)) << Qt::endl; qDebug() << u8"Value of qBlue(0): " << qBlue(img8to16.pixel(0,0)) << Qt::endl;for(int y = 0;y<10;y++) //print the value of each point { qDebug() << u8"Value of " << y+1 << "Rows: " << img8to16.pixel(0,y) << img8to16.pixel(1,y)<<img8to16.pixel(2,y) <<img8to16.pixel(3,y) <<img8to16.pixel(4,y)<<img8to16.pixel(5,y) <<img8to16.pixel(6,y) <<img8to16.pixel(7,y)<<img8to16.pixel(8,y) << Qt::endl; }for(int i = 0;i<200;i++) //print the value of each point through pointer! { qDebug() << *(pGrayscale16+i) ; //if((i+1)%9==0) qDebug() << Qt::endl; }以上的部分輸出結果為:
Total bytes of Image: 720 Bytes per Line of Image: 72 Value of scanLine(0): 11 Value of qAlpha(0): 255 Value of qRed(0): 11 Value of qGreen(0): 11 Value of qBlue(0): 11 Value of 1 Rows: 4278913803 4278979596 4279045389 4279111182 4279176975 4279242768 4279308561 4279374354 4279440147 ......11 11 11 11 11 11 255 255 12 12 12 12 12 12 255 255 13 13 13 13 13 13 255 255 14 14 14 14 14 14 255 255 15 15 15 15 15 15 255 255 16 16 16 16 16 16 255 255 17 17 17 17 17 17 255 255 18 18 18 18 18 18 255 255 19 19 19 19 19 19 255 255 //第一行21 21 21 21 21 21 255 255 ......可以看到將Format_Indexed8直接轉換成Format_RGBX64之后,每行變成了72字節,這是因為對于RGBX64,The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16),即位深為64(不用對齊了), 64?9/8=7264*9/8=7264?9/8=72,此時的格式轉換相當于將8位圖像(0~255)映射到16位(0~65535)。雖然只是簡單的復制了數據,例如對第一個數據點(原來為11),轉換之后為:11 11 11 11 11 11 255 255 ,對應RRGGBBAA,但是這樣直接轉為Format_RGBX64是可以的。(11*256+11,二進制形式沒有變化)
另外直接轉換成Format_Grayscale16之后,除了每行字節變為20字節以外,數據的組織形式仍以簡單的復制進行,例如同樣打印第一行數據得到的結果為:
11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 15 255
最后的15和255同樣是數據對齊生成的隨機值,此時的數據對齊為:(9?16+31)/32×4=20(9*16+31)/32×4=20(9?16+31)/32×4=20
三、Format_RGBX64詳解
除了將8位圖像轉換成16位圖像, 也可以自己給16位圖像賦值
新建一個Format_RGBX64格式圖像:
QImage qimgRGB16(9,10,QImage::Format_RGBX64); //新建一個10行9列的QImage數組,會自動數據對齊,每行20字節for(int y =0;y<10;y++) {for(int x=0;x<9;x++){//qimgRGB16.setPixel(x, y, qRgb(20, 20, 120)); //結果同下qimgRGB16.setPixelColor(x,y, QColor::fromRgba64(5140, 5140, 30840));} }uchar * pRGB16 = qimgRGB16.scanLine(0); int y = 0; qDebug() << u8"Value of " << y+1 << "Rows: " << qBlue(qimgRGB16.pixel(0,y)) << qimgRGB16.pixel(1,y)<<qimgRGB16.pixelColor(2,y) <<qimgRGB16.pixel(3,y) <<qimgRGB16.pixel(4,y)<<qimgRGB16.pixel(5,y) <<qimgRGB16.pixel(6,y) <<qimgRGB16.pixel(7,y)<<qimgRGB16.pixel(8,y) << Qt::endl; }for(int i = 0;i<24;i++) {qDebug() << *(pRGB16+i) ; }以上的輸出結果為:
Value of 1 Rows: 120 4279506040 QColor(ARGB 1, 0.0784314, 0.0784314, 0.470588) 4279506040 4279506040 4279506040 4279506040 4279506040 427950604020 20 20 20 120 120 255 255 20 20 20 20 120 120 255 255 20 20 20 20 120 120 255 255我們是通過setPixelColor這個函數給圖像賦值的(效率很低),上述賦值為:setPixelColor(x,y, QColor::fromRgba64(5140, 5140, 30840));即會把(5140,5140,30840)這個RGB值賦給指定像素點,這樣當按照字節打印值得時候就打印出了20 20 20 20 120 120 255 255的結果,20二進制為:0001 0100,而兩個20為:0001 0100 0001 0100,其對應的值為5140,說明這是正確的。該賦值相當于setPixel(x, y, qRgb(20, 20, 120));相同的原因因該是Qt默認qRgb只支持8位,另外在實驗中qRgb超過255就會出問題。
另外在輸出的時候同樣有所不同,qBlue(qimgRGB16.pixel(0,0))、 qimgRGB16.pixel(1,0) 、qimgRGB16.pixelColor(2,0)這三種輸出同樣的像素點的結果分別為:120 、 4279506040 、 QColor(ARGB 1, 0.0784314, 0.0784314, 0.470588),第一個120剛才也說了應該是qRgb默認的8位深度;4279506040對應的十六進制為:FF141478正好對應ARGB,即255、20、20、120;而QColor(ARGB 1, 0.0784314, 0.0784314, 0.470588)這種形式也可以換算,例如0.0784314*65536=5140,0.0784314*256=20。所以可以通過setpixel或者setpixelColor的方法設置圖像某個像素點的數值,利用以上方法可以得某點的數值,但是setpixel或者setpixelColor都是官方所不推薦的(效率慢),官方聲明如下,另外該方法得到某點處的像素值也需要通過換算,極為麻煩。
void QImage::setPixelColor(const QPoint &position, const QColor &color)Sets the color at the given position to color. If position is not a valid coordinate pair in the image, or the image's format is either monochrome or paletted, the result is undefined. Warning: This function is expensive due to the call of the internal detach() function called within; if performance is a concern, we recommend the use of scanLine() or bits() to access pixel data directly. This function was introduced in Qt 5.6. void QImage::setPixel(const QPoint &position, uint index_or_rgb)Sets the pixel index or color at the given position to index_or_rgb. If the image's format is either monochrome or paletted, the given index_or_rgb value must be an index in the image's color table, otherwise the parameter must be a QRgb value. If position is not a valid coordinate pair in the image, or if index_or_rgb >= colorCount() in the case of monochrome and paletted images, the result is undefined. Warning: This function is expensive due to the call of the internal detach() function called within; if performance is a concern, we recommend the use of scanLine() or bits() to access pixel data directly. See also pixel() and Pixel Manipulation.所以最好使用官方鎖推薦的操作像素的方法:scanLine(),這也是以上為什么多次用到該函數的原因,下面以Format_Grayscale16為例進行分析。
四、Format_Grayscale16格式
QImage qimg16(9,10,QImage::Format_Grayscale16); //新建一個10行9列的QImage數組,會自動數據對齊,每行20字節//第一種賦值 for(int y =0;y<10;y++) {for(int x=0;x<9;x++){QColor c(50, 50, 120);qimg16.setPixel(x,y, c.rgba()); //Note: (x,y) means coordinate,不是行列下標!!!! (y+1)*6000+x+1} }uchar * pGrays16 = qimg16.scanLine(0);int y = 0; qDebug() << "Value of 1 Rows: " << qimg16.pixelColor(0,y) << qimg16.pixel(0,y) << qimg16.pixel(1,y)<<qimg16.pixel(2,y) <<qimg16.pixel(3,y) <<qimg16.pixel(4,y)<<qimg16.pixel(5,y) <<qimg16.pixel(6,y) <<qimg16.pixel(7,y)<<qimg16.pixel(8,y) << Qt::endl;for(int i = 0;i<200;i++) //print the value of each point through pointer! {qDebug() << *(pGrays16+i) ;//if((i+1)%9==0) qDebug() << Qt::endl; }以上輸出的結果為:
Value of 1 Rows: QColor(ARGB 1, 0.235294, 0.235294, 0.235294) 4282137660 4282137660 4282137660 4282137660 4282137660 4282137660 4282137660 4282137660 4282137660 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 205 205與之前的類似,唯一不同的是有了數據對齊,位深不同。Grayscale16為灰度圖,沒有ARGB通道,所以當我們使用QColor c(50, 50, 120);
 qimg16.setPixel(x,y, c.rgba());賦值的時候,Qt會自動將該RGB值轉換成灰度值,這里試過幾個不同的值轉成灰度值,大概系數應該是0.34R+0.5G+0.15B,沒有仔細的算(因為是生成的灰度值是經過取整的),所以灰度圖不應該使用qRgb的形式來賦值,因為計算機存儲一般用的是小端表示,所以除了上述方法還可以直接指定數值,例如:
以上賦值方法是分別在高8位和低8位兩個字節賦值,同樣此種操作復雜。
所以!!操作像素怎么使用官方推薦的scanLine()函數呢,因為scanLine()函數的原型為:uchar *QImage::scanLine(int i),也就是該函數返回一個uchar*類型的指針,因為我們操作的是16位圖像, 每個像素占兩個字節,所以應該將指針變成ushort*類型即可!如下:
QImage qimg16(9,10,QImage::Format_Grayscale16); //新建一個10行9列的QImage數組,會自動數據對齊,每行20字節for(int i = 0;i<10;i++) //行 {uchar * pGrays16Tmp = qimg16.scanLine(i);ushort * pushortTmp = reinterpret_cast<ushort*>(pGrays16Tmp);for(int j=0; j<9;j++) //列{*(pushortTmp+j)= 52726; //賦值} }uchar * pGrays16 = qimg16.scanLine(0); ushort * pushort = reinterpret_cast<ushort*>(pGrays16); for(int i = 0;i<100;i++) //print the value of each point through pointer! {qDebug() << *(pushort+i) ; //輸出像素值//if((i+1)%9==0) qDebug() << Qt::endl; }輸出結果為:
52726 52726 52726 52726 52726 52726 52726 52726 52726 52685 ......這樣就完成了16位圖像的操作,最后的52685是對齊的隨機數,所以之前說讀取像素的時候最好不要用scanLine(0)從頭到尾!
總結
以上是生活随笔為你收集整理的Qt中QImage用于16位图像的显示,QImage数据对齐的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: SAP中货源清单创建的几种方法
 - 下一篇: c#将list集合转换为datatabl