zoukankan      html  css  js  c++  java
  • QImage 图像格式小结

    原地址:http://tracey2076.blog.51cto.com/1623739/539690

    嗯,这个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, Formatformat )

         嗯,这个函数就是从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中将是补齐数据的数组,由此构造的图像不会有任何问题。

    好了,终于总结完了,有很多小的问题,我不想写了,应该够用了,如果有具体其他问题,大家再仔细想想,肯定能解决的,哈哈

  • 相关阅读:
    ArrayList用法
    MessageBox
    将文本文件导入Sql数据库
    在桌面和菜单中添加快捷方式
    泡沫排序
    Making use of localized variables in javascript.
    Remove double empty lines in Visual Studio 2012
    Using Operations Manager Connectors
    Clear SharePoint Designer cache
    Programmatically set navigation settings in SharePoint 2013
  • 原文地址:https://www.cnblogs.com/mmix2009/p/3585768.html
Copyright © 2011-2022 走看看