QImage save() to store to memory (e.g. cache)
-
The save() function calls the QImageWriter and loads an handler to "encode" the image into a specific image format: e.g. jpg or png.
What if I just want to store the QImage as raw pixels? e.g. let's say I want to store it into a cache - for reloading.
Right now, here is what I have:
- Read raw bytes from image file (or network) e.g. N bytes of .jpg
- Call QImage::loadFromData() which "decodes" that into M bytes of 2D array of pixels.
- I store that QImage by calling save() which re-encodes it (e.g. N bytes of .jpg)
- Later, when someone wants that image, the call the cache and we then call loadFromData() which re-decodes that raw image N bytes into M bytes of pixels.
etc..
So, I am incurring decode and re-encode many times!?
i.e. I am less concerned about memory and more about encoding / decoding time.
e.g. Sample .jpg file is 48 KB and maps to 332 x 328 pixels or 108,896 pixels. If I have 3 bytes per pixel (RGB), that's 319Kb. I am guessing that decoding 319Kb into 48Kb and the re-encoding it into 319Kb is MORE expensive in CPU that just copying those 319Kb; right?So, what is the best way to minimize 'encoding' / 'decoding'?
Can I get QImage just be a pass-through?
Or, Can I simply serialize QImage into raw bytes (e.g. void*) and rebuild it afterwards? -
Hi, welcome to the forum.
Why do you need QImage at all in that scenario? Why not just
- Read raw bytes from image file (or network) e.g. N bytes of .jpg
- Store the N bytes in a cache.
Or, if you prefer to store decoded data, you can get the decoded image data via QImage::bits() and then reconstruct the QImage from it using loadFromData (there's no jpg encoding/decondig in that, just a copy).
But if you're reconstructing QImage anyway why not just cache the whole QImage? If you're drawing it you can even replace QImage with QPixmap and use QPixmapCache that has all that already implemented.
-
@Chris-Kawa said in QImage save() to store to memory (e.g. cache):
QImage::bits()
First, Thank you for the prompt reply - this forums is quite active! :)
I need QImage because I used it to "paint" the pixels onto an area.
Ideally, I want to decode only once.
I like the idea of using QImage to decode the raw (e.g. N bytes of jpg into M images of pixel bytes). That way, I would only decode the raw image (i.e. jpg into pixels) once; and re-load pixels over and over which would simply be memory copy.In that scenario, what's the format text string to use to rebuild a QImage using loadFromData()?
There seems to be lack of clarify about what "format" is?- in "loadFromData()" and "fromData()" the "format" argument is a "const char*"; aka text string. I am guessing "PNG", "JPG" etc..
- the "format()" function returns a "QImage::Format" - an enum value.
It seems that a constructor would need to be used with "QImage::Format" enum rather than one of the "loadXXX" functions right? (i.e. It seems that the "loadXXX" functions are really "load-and-decode-from-raw-image-file-data" right?).
So, I think this is the right way:
- Read raw image data (i.e. read from .jpg file or download from network N jpg bytes).
- Use QImage to "decode" by using "loadFromData()" or "fromData()" passing-in those raw image bytes (e.g. N jpeg bytes)
- This causes the image to get decoded and stored internally as an array of pixels (e.g. 32 bits = 4 bytes per pixel).
- Store the QImage::Format enum (i.e. by calling "format()"), width and height and raw pixel bits (by calling "QImage::bits()") into my cache.
- On extraction from cache, call QImage constructor to recreate a new QImage object using height x width , format enum and array of bytes.
Does that sound reasonable?
-
@Nicolas-Duchastel-de-Montrouge Yup, sorry, I misspoke about loadFormData. It is indeed for decoding image formats. For raw uncompressed bits you'd use a constructor. Just keep in mind that the QImage constructors taking raw data don't make a copy of it but use that data directly, so if it's a data stored in the cache you need to make sure it is not released as long as that QImage using it exists. Otherwise you need to make a copy before constructing QImage from it and provide the cleanup function.
Alternatively you can just store the whole QImage object in the cache and don't bother with reconstructing it every time.
-
Christian Ehrlicher Lifetime Qt Championreplied to Nicolas Duchastel de Montrouge on last edited by
@Nicolas-Duchastel-de-Montrouge said in QImage save() to store to memory (e.g. cache):
Does that sound reasonable?
No, as already said many times - directly put the QImage in your cache.
-
How can I place a QImage into my cache? Doesn't it have a bunch of pointers to data etc?
-
Christian Ehrlicher Lifetime Qt Championreplied to Nicolas Duchastel de Montrouge on last edited by
@Nicolas-Duchastel-de-Montrouge said in QImage save() to store to memory (e.g. cache):
How can I place a QImage into my cache?
By simply adding it?
QVector<QImage> myVec; myVec += myImage;
-
My intuition is that there's a > 90% chance that what you want to do won't help with anything, and a very strong probability that it will make things worse, and the extra complexity you are trying to add increases the surface area for potential bugs. I'd be very cautious with thinking through what you describe.
The OS already keeps an in-memory cache of contents of the disk when it reads. So if your application tries to re-read a file from disk, it's likely that it is already in the OS page cache. Keeping it in memory in your process means that you are storing it in memory that other applications (or other instances of your own application in another process) can't benefit from, and takes up space that would otherwise be used by the OS page cache. In a lot of scenarios, that's a strict pessimization.
Storing the decoded images in memory can make sense because you can use those directly. It's a classic time/space tradeoff. Keeping the decoded images is larger, but at least you trade the time spend decoding the JPEG. You can use something like a QCache object for managing the cache fairly automatically: https://doc.qt.io/qt-5/qcache.html
e.g. Sample .jpg file is 48 KB and maps to 332 x 328 pixels or 108,896 pixels. If I have 3 bytes per pixel (RGB), that's 319Kb. I am guessing that decoding 319Kb into 48Kb and the re-encoding it into 319Kb is MORE expensive in CPU that just copying those 319Kb; right?
Round-tripping to JPEG is enormously slower than just copying the image, because the encoding process has to read all of the pixels in the source image, just like copying does. JPEG is also lossy, so you lose quality with this strategy.
Also, QImage is "implicitly shared" so copying a QImage object won't necessarily copy the pixels unless you do something to modify the image. If you just pass it to a function or something, the copy is "cheap" and just references the existing pixels.
So, what is the best way to minimize 'encoding' / 'decoding'?
The best way to minimize it is not to do it.