Qt中QImage用于16位图像的显示,QImage数据对齐

之前总结过QImage类,https://blog.csdn.net/weixin_43294620/article/details/122419099?spm=1001.2014.3001.5501

但是对于16位图像的显示没有详细说明,Qt也支持16位的图像,例如QImage::Format_Grayscale16QImage::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)这三种输出同样的像素点的结果分别为:1204279506040QColor(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的形式来赋值,因为计算机存储一般用的是小端表示,所以除了上述方法还可以直接指定数值,例如:

uchar * pGrays16 = qimg16.scanLine(0);
for(int i = 0;i<200;)
{*(pGrays16+i)=1;i = i+2;
}for(int i = 1;i<200;)
{*(pGrays16+i)=255;i = i+2;
}

以上赋值方法是分别在高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数据对齐相关推荐

  1. qt中使用qwt实现实时曲线显示

    最近做一个飞控的上位机,需要实时在一个图中显示多条曲线,并且这些曲线还可以在任何时刻隐藏和显示.于是借鉴qgc地面站中显示mavlink消息的曲线显示方法,用qwt实现了这一功能. 最终效果 可以设置 ...

  2. linux下静态编译mupdf,在Qt中调用Mupdf库进行pdf显示

    2018.5.10 更新内存对齐说明 感谢知乎网友@孤独子狮指出QImage处需要考虑内存对齐的问题.因为本人缺乏跨平台.图形库开发经验,所以在调试成功后就没有深入探究. 主要修改了QImage的构造 ...

  3. Qt中的模型视图设计模式

    文章目录 1 初探Qt中的模型视图设计模式 2 模型视图中的索引 3 模型中的数据组织方式初探 1 初探Qt中的模型视图设计模式 模型视图设计模式的核心思想: 模型(数据)与视图(显示)相分离. 模型 ...

  4. 【QT】QT从零入门教程(十五):QImage和Mat的转换

      QT处理图像用的是QImage类,而OpenCV处理图像则是使用cv::Mat类,如果要在QT中运用OpenCV,那就需要进行QImage和Mat的相互转换. // cv::Mat转换成QImag ...

  5. iOS开发中QQ好友列表下拉显示全部好友实现思路

    在QQ的好友页面有一个点击各个分类,然后展示分类中所有好友的效果,今天就说一说实现该效果的一个思路. 这个页面肯定是需要一个UITableView的,在tableview的代理方法中要实现分区的一个方 ...

  6. elementUI中,实现一个单元格内显示两行数据,并用其中一个数据进行排序。

    最近在公司中,有这样一个需求,表格中,一个单元格里面显示两行数据,并且可以使用其中一行进行排序,其中数据的样式也要实时变动.类似于下图 这样的话,elementUI中自带的prop就不适合了.所以,需 ...

  7. php 获取数据库函数吗,如何正确理解PHP获取显示数据库数据函数

    在运用PHP语言对数据库进行操作的时候,我们将会用到mysql_X这一函数库.下面我们将为大家详细介绍有关PHP获取显示数据库数据函数的函数. PHP获取显示数据库数据函数之 mysql_result ...

  8. 如何在二维码下面显示对应数据

    在使用条码二维码软件制作标签时,经常见到的条形码下面是有对应数据的,但是二维码是没有自带显示数据的,那么如果我们想要使二维码也显示同样的数据该如何操作呢,下面我们就中琅二维码软件来看下具体如何操作? ...

  9. linux qt应用程序全屏,QT中MDI应用程序中更改子窗口大小或是全屏显示子窗口的方法...

    1.QT中窗口部件QWidget成员函数showFullScreen();是用于将窗口部件全屏显示. 但是他只对窗口模式的部件有用.子窗口的特征是 Qt::SubWindow,不是独立的窗口.因此对其 ...

  10. QT中循环显示图片和简单的显示图片

    请关注我的github https://github.com/linqiaozhou 以下实例代码不久后将会上传到我的github 这是我最近一个项目中的部分代码 //以下是简单的在QT中显示图片的代 ...

最新文章

  1. 一个项目涉及到的50个Sql语句(整理版)
  2. MapReduce英语面试
  3. linux下dns设置详解
  4. zblog php搜索页面,Z-Blog PHP实现搜索分页
  5. vscode 新建cpp文件_利用vscode搭建c
  6. 非常适合小白的 Asyncio 教程
  7. mysql开启事务_MySQL入门之事务(上)
  8. zabbix2.2.3 VMware Vsphere exsi监控配置步骤
  9. Python中的符号
  10. 深度学习-服务端训练+android客户端物体识别实战(caffe入门教程+mobilenet+ncnn+android)
  11. Java HashMap元素取值
  12. 京东探索研究院 34 篇论文入选 CVPR 2022
  13. Linux下的进程池(2)
  14. Linux-系统编程-知识点概述
  15. python代码翻译器-10 行代码,Python 教你自制屏幕翻译工具,有逼格!!
  16. 通知NSNotificationCenter
  17. cdr多页面排版_CDR文字排版实战图文教程,CorelDRAW文字排版有哪些技巧?
  18. 超硬核!十万字c++题,让你秒杀老师和面试官(上)
  19. VMware虚拟机不能上网了怎么办
  20. linux 内核更改,linux_kernel修改、编译、加载

热门文章

  1. 修改SRVINSTW支持sys文件
  2. 华为网关服务器型号,02311CWM CN21ITGC SP212 I350-T4 华为服务器四口千兆网卡
  3. windows和Linux下西部数据C1门解决方法
  4. android SD卡文件夹
  5. windows 进程之csrss.ext
  6. 用户登陆问题,session.invalidate销毁session
  7. 【忆往昔】我欠老牛两本书
  8. html嵌入百度播放器
  9. 《过程控制系统》习题整理
  10. redis缓存数据库技术