Problems with large QImage
Asked Answered
G

4

5

I'm pretty new to C++/Qt and I'm trying to create an application with Visual Studio C++ and Qt (4.8.3). The application displays images using a QGraphicsView, I need to change the images at pixel level.

The basic code is (simplified):

QImage* img = new QImage(img_width,img_height,QImage::Format_RGB32);
while(do_some_stuff) {
  img->setPixel(x,y,color);
}
QGraphicsPixmapItem* pm = new QGraphicsPixmapItem(QPixmap::fromImage(*img));
QGraphicsScene* sc = new QGraphicsScene;
sc->setSceneRect(0,0,img->width(),img->height());
sc->addItem(pm);
ui.graphicsView->setScene(sc);

This works well for images up to around 12000x6000 pixel. The weird thing happens beyond this size. When I set img_width=16000 and img_height=8000, for example, the line img = new QImage(...) returns a null image. The image data should be around 512,000,000 bytes, so it shouldn't be too large, even on a 32 bit system. Also, my machine (Win 7 64bit, 8 GB RAM) should be capable of holding the data.

I've also tried this version:

uchar* imgbuf = (uchar*) malloc(img_width*img_height*4);
QImage* img = new QImage(imgbuf,img_width,img_height,QImage::Format_RGB32);

At first, this works. The img pointer is valid and calling img->width() for example returns the correct image width (instead of 0, in case the image pointer is null). But as soon as I call img->setPixel(), the pointer becomes null and img->width() returns 0.

So what am I doing wrong? Or is there a better way of modifying large images on pixel level?

Regards, David

Gosser answered 26/11, 2012 at 23:2 Comment(4)
Try to manually zero the malloc'd memory first to see if the allocation is actually working.Difficile
Take a look at this: Qt Project Wiki: Loading Large ImagesLigamentous
Remember that on a 32 bit windows application the default largest allocation you can make a result of address space fragmentation will be around 1.2GB (of the 2GB application address space) without using the /LARGEADDRESSAWARE linker flag and/or rebasing the dlls your application uses to reduce the fragmentation.Abstracted
---UPDATE--- Meanwhile i was able to narrow down the problem. Now I can create images up to ~16000x8000 pixels and can even use img->setPixel(). Previously I updated the image during "rendering". It seems like sending the image to the scene makes it impossible to use setPixel() again (the image becomes "null"). With only sending the image to the graphicsView (after the while-loop) it works. Now I just need to figure out how I can update the image during "rendering" nethertheless.Jemadar
S
1

Your second approach is the right way to go. The problem you are having with it is when you call setPixel(), QImage makes a copy of the external buffer you provided and runs out of memory for it.

Try changing the pixel value directly in the supplied buffer. You can use scanLine() to get the pointer to the buffer of a line. I would not use setPixel() anyway since it's really slow.

Sorn answered 27/11, 2012 at 19:59 Comment(0)
T
4

QImage supports a maximum of 32768x32768 px images (signed short). This follows from the condition: width * height * colordepth < INT_MAX (4 billion) -> 32768 * 32768 * 4 = 4 billion. The second condition is of course that malloc is able to allocate the requested memory.

If you really need bigger images you will have to use another wrapper or split into multiple QImage's.

Telemeter answered 26/11, 2012 at 23:38 Comment(0)
G
2

I tracked down the problem. I just forgot to add the /LARGEADDRESSAWARE flag to the Linker options.

I did also really appreciate Stephen Chu's answer with the scanLine() hint. First it saves some memory, second it's really a lot faster.

Now I can safely create images up to 32000x16000 pixel which was my desired aim.

Gosser answered 28/11, 2012 at 22:38 Comment(0)
S
1

Your second approach is the right way to go. The problem you are having with it is when you call setPixel(), QImage makes a copy of the external buffer you provided and runs out of memory for it.

Try changing the pixel value directly in the supplied buffer. You can use scanLine() to get the pointer to the buffer of a line. I would not use setPixel() anyway since it's really slow.

Sorn answered 27/11, 2012 at 19:59 Comment(0)
A
0

As of Qt 6, you can just call:

  QImageReader::setAllocationLimit(4096);

Check the related doc here. In the above example I set the limit at 4GB. This value can be overridden by setting the env variable QT_IMAGEIO_MAXALLOC.

Artilleryman answered 25/11, 2023 at 19:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.