【转】QImage 图像格式小结

摘要:
构造图像:,QImagemyImage1=QImage;根据文件名打开图像。如果图像本身是32位或24位,则程序中的图像是32位。如果图像本身是8位或1位,则程序中的对应图像是8位或者1位。宽度表示图像宽度,高度表示图像高度。

图像的数据是以字节为单位保存的,每一行的字节数必须是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中将是补齐数据的数组,由此构造的图像不会有任何问题。

免责声明:文章转载自《【转】QImage 图像格式小结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇收集vcftools所有用法爱快路由器的一些注意事项硬件配置+多线负载均衡下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

Debian 命令行方式配置网络

确保未在文件/etc/network/interfaces中手动配置网络,即应删除上述1和2中配置的内容。打开nmtui的图形界面并直接进行配置。当然,您也可以在此处设置无线连接。...

ThinkPHP

ThinkPHP的开发模式是define//Debug mode define//当运行模式控制器的操作系统找不到请求的方法时,它将定位__Empty()方法处理。使用此机制,我们可以统一处理用户请求的所有不存在的操作。模块分组大A函数和大R函数有什么区别?关联数组易于操作,信息量相对较大...

QMap与QHash

Qt提供两个主要的关联容器类:QMap和QHash。QMap的K和T有一对方便的函数keys()和values(),它们在处理小数据集时显的特别有用。QMap重载了value,返回一个给定键多有值的QList列表。在内部,它们都依赖于QHash,且都像QHash一样对K的类型有相同的要求。...

redis make报错

所以添加参数:makeMALLOC=libc第二种类型:makeCFLAGS=“-march=x86-64”在README中有此段。...

电脑不识别USB blaster驱动问题

电脑不识别USB blaster,如下图: 解决办法:手动更新...

Linux系统添加永久静态路由的方法

按照Linux启动的顺序,rc本地的内容在Linux中的所有服务启动后执行。也就是说,local的内容在netfs之后执行。也就是说,当netfs启动时,不会添加服务器上的静态路由,因此无法成功装载netfs。...