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_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 = ( w i d t h × b i t c o u n t + 31 ) / 32 × 4 W=(width×bitcount+31)/32×4 W=(width×bitcount+31)/32×4,其中width为图像的实际宽度,bitcount是图像的位深,例如以上图像的位深为8,W是计算得到的程序中图像每行的字节数。所以创建9*10的图像时,实际的宽度 W = ( 9 × 8 + 31 ) / 8 = 12 W=(9×8+31)/8=12 W=(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 = 72 64*9/8=72 649/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 (916+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 4279506040
    
20 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)从头到尾!

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: twain是一种用于扫描仪和摄像机的标准协议,它允许应用程序访问扫描仪和摄像机的功能。QImageQt用于处理图像的类。 如果要将twain彩色图数据复制到QImage,可以使用下面的步骤: 1. 首先,使用twain API打开扫描仪或摄像机,并将其设置为获取彩色图像数据。 2. 然后,使用twain API读取图像数据,并将其存储在缓冲区。这些数据通常是以RGB格式存储的。 3. 接下来,创建一个空的QImage对象。设置QImage的宽度和高度为扫描到的图像的宽度和高度。 4. 现在,遍历缓冲区的图像数据,并将其复制到QImage。注意,每一行必须对齐。可以使用QImage的scanLine()函数获取每一行的指针,并使用memcpy()函数将数据复制到QImage。 下面是一个例子,展示了如何将twain彩色图数据复制到QImage: ``` // 使用twain API打开扫描仪,并设置为获取彩色图像数据 OpenScanner(); SetColorMode(TWPT_RGB); // 读取图像数据,并将其存储在缓冲区 unsigned char* buffer = ReadImage(); int width = GetImageWidth(); int height = GetImageHeight(); // 创建一个空的QImage对 ### 回答2: 要将Twain彩色图像数据复制到QImage对齐每一行,可以按照以下步骤进行操作。 首先,我们需要了解Twain彩色图像数据的格式。Twain彩色图像数据通常采用RGB格式,即每个像素由红、绿、蓝三个分量构成。 然后,我们需要创建一个QImage对象,并指定其宽度、高度和格式。对于Twain彩色图像数据,可以选择QImage::Format_RGB32格式,它与RGB格式兼容。 接下来,我们需要遍历Twain彩色图像数据并复制到QImage。假设Twain彩色图像数据以一维数组data表示,并已知图像的宽度width和高度height。 可以使用以下代码实现复制和对齐每一行的功能: ```cpp QImage image(width, height, QImage::Format_RGB32); // 创建QImage对象 for (int y = 0; y < height; ++y) { QRgb* scanLine = reinterpret_cast<QRgb*>(image.scanLine(y)); // 获取当前行的扫描线 for (int x = 0; x < width; ++x) { int index = y * width + x; // 计算当前像素在一维数组的索引 // 从Twain彩色图像数据获取红、绿、蓝三个分量的值 int red = data[index * 3]; int green = data[index * 3 + 1]; int blue = data[index * 3 + 2]; // 使用QRgb类型构造像素值,并设置到QImage的当前像素位置 scanLine[x] = qRgb(red, green, blue); } } ``` 在以上代码,我们使用scanLine()函数获取QImage的第y行的扫描线,然后将Twain彩色图像数据对应像素的红、绿、蓝三个分量的值设置到这个扫描线的每个位置上。由于scanLine()返回的是一个uchar指针,我们需要将其转换为QRgb指针,以便能够直接设置每个像素的值。 注意,以上代码假设Twain彩色图像数据按照RGB顺序存储,每个分量占用一个字节,且没有其他的行对齐要求。如果Twain彩色图像数据的存储格式与此不同,需要相应地修改代码。 通过以上步骤,即可将Twain彩色图像数据复制到QImage,并保证每一行的对齐。 ### 回答3: 将twain彩色图数据复制到QImage并每一行对齐的例子如下: 首先,我们需要创建一个QImage对象,以及定义图像的宽度、高度和图像格式。假设图像宽度为width,高度为height,图像格式为RGB888。 ```cpp QImage image(width, height, QImage::Format_RGB888); ``` 接下来,我们需要复制twain彩色图数据到QImage对象。假设twain彩色图数据的格式为BGRA8888。 ```cpp // 假设twain彩色图数据保存在twainData数组 unsigned char* twainData; // 获取twain一行图像数据所占的字节数 int rowBytes = width * 4; // 遍历图像每一行 for (int y = 0; y < height; y++) { // 复制一行图像数据到QImage memcpy(image.scanLine(y), twainData + y * rowBytes, rowBytes); } ``` 在上述代码,我们使用memcpy函数将twain一行图像数据复制到QImage。通过调用image.scanLine(y)获取到每一行的指针,并将twain图像数据复制到该指针位置。 注意,对于每一行图像数据对齐,我们使用了rowBytes变量来计算每一行数据所占字节数。twain彩色图数据的每一个像素占4个字节,因此一行数据的字节数为图像宽度乘以4。 最后,我们可以使用QImage对象进行进一步的处理或显示,例如: ```cpp // 显示QImage QLabel* label = new QLabel(); label->setPixmap(QPixmap::fromImage(image)); label->show(); ``` 以上就是将twain彩色图数据复制到QImage并每一行对齐的详细例子。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值