您当前的位置: 首页 >  qt
  • 2浏览

    0关注

    483博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Qt开发高级进阶:高速读写图片数据之QImage内存的直接操作

高精度计算机视觉 发布时间:2022-05-12 22:43:16 ,浏览量:2

Qt中有两个最常用的类,一个是QPixmap,一个是QImage,当然还有QBitmap,QPicture之类的。关于QPixmap和QImage,官方的解释是这样的,

Qt provides four classes for handling image data: QImage, QPixmap, QBitmap and QPicture. QImage is designed and optimized for I/O, and for direct pixel access and manipulation, while QPixmap is designed and optimized for showing images on screen. QBitmap is only a convenience class that inherits QPixmap, ensuring a depth of 1. Finally, the QPicture class is a paint device that records and replays QPainter commands. Because QImage is a QPaintDevice subclass, QPainter can be used to draw directly onto images. When using QPainter on a QImage, the painting can be performed in another thread than the current GUI thread.

大概是说QPixmap主要用于显示,而QImage则是图像的物理存储信息。

如果我们要对QImage进行像素级的图片读写,通常要用到setpixel之类的函数,但每读一个像素,需要调用函数级堆栈,如果要对整张图像进行算法上的变换,效率是完全不能接受的。

所以,我们必须要对raw data(内存数据块)直接操作。下面是一段测试代码,这里我只测试了Grayscale8,ARGB32和Mono格式,RGB24等大家可以自己去尝试,如下,

    gImgViewer = new QGraphicsView();
    gScene = new QGraphicsScene();    
    gScene->setSceneRect(0, 0, 800, 600);
    gImgViewer->setScene(gScene);

    // 在mainwindow.cpp中,构建好QGraphicsScene和QGraphicsView之后,添加下面这一段代码即可

    // 测试,使用不同格式时注意修改步长
    QImage::Format fmt = QImage::Format_ARGB32; //:Format_Grayscale8; //QImage::Format_Mono;
    QImage *image = new QImage(800, 600, fmt);
    int step = image->bytesPerLine(); //image->width();
    int wd = image->width();
    int ht = image->height();
    //image->data_ptr()==QImageData
    uchar* ptr = image->bits();
    uchar* ptr_var = ptr;
    uchar **pptr = new uchar*[ht];
    for (int y = 0; y < image->height(); y++) {
        ptr_var += step;
        pptr[y] = ptr_var; //&(((uchar*)ptr_var)[0]);
    }
    if(QImage::Format_Mono == fmt){
        assert(step*8 == wd);  // NOTE: for mono, each pix has 1 bit
    }else if(QImage::Format_Grayscale8 == fmt){
        assert(step == wd);    // NOTE: for mono, each pix has 1 bit
        for(int x=200; xaddPixmap(QPixmap::fromImage(*image));
    delete [] pptr;

这里最关键的是在获取到内存的地址image->bits()之后,通过指针的形式pptr把1维数据变成2维数据(当然也可用scanline()函数),然后直接以2维数据的形式对数据进行存储与读取。本例源码的grayscale8显示结果 如下,

 随便找一张800*600的图片,然后使用源码中的ARGB32覆盖在图片上面,可以看到当alpha设置为100时的中间部分透明度较高,效果如下,

 图中的四周灰色是没有初始化的内存,中间白色就是我们源码中逐个像素填充的颜色色(255)。

值得说明的是,

(1)直接使用源码中的代码貌似存在内存泄露,当pixmap接收QImage的时候,会自己在内存中保存一份copy,此时指针形式创建的内存并没有释放,测试如下源码,会发现scene->clear这句只释放了一半内存,另一半是由new创建的,并没有释放。使用时不用的内存区需要自己去删除。

    for (int i = 0; i < 50; i++) {
        QImage* image = new QImage(8000, 6000, fmt);
        scene->addPixmap(QPixmap::fromImage(*image));
    }
    scene->clear();

(2)源码中对数据进行了维度变换。对数据进行维度变换有好几种办法,这里只选了最常用的一种,更多具体可参考

C++ 中多维数组之间的快速转换(以1维2维数组的相互转换为例说明)_高精度计算机视觉的博客-CSDN博客在图像处理中,大规格的数组之间的转换是家常便饭。c++中多维数组在本质上就是一个地址问题,普通情况下,可以通过类似如下方法进行暴力转换int arr[50];int main(){unsigned i, j;for (i = 0; i < 50; i++) {arr[i] = 2 * i;}int bruteArray[7][7];for (i =...https://blog.csdn.net/tanmx219/article/details/93874224

 最后,如何获取QGraphicsPixmapItem中的QImage?

我们看一下下面这段测试代码,把前面的白色变成 红色,透明度依旧保持为100,

    QPixmap pmp = pix_item->pixmap();
    QImage image_t = pmp.toImage();
    fmt = image_t.format();
    if(QImage::Format_ARGB32_Premultiplied == fmt){
        *image = image_t.convertToFormat(QImage::Format_ARGB32);
        wd = image->width();
        ht = image->height();
        step = image->bytesPerLine();
        assert(step == wd * 4);    // NOTE: for mono, each pix has 1 bit
        ptr = image->bits();
        ptr_var = ptr;
        for (int y = 0; y < image->height(); y++) {
            ptr_var += step;
            pptr[y] = ptr_var; //&(((uchar*)ptr_var)[0]);
        }
        for(int x=200*4; x            
关注
打赏
1661664439
查看更多评论
0.0491s