PDA

View Full Version : qPixmap retrieve size ( MB/KB ), scalling image performance



Talei
3rd April 2010, 09:53
Hello,
I want to ask if there is a easy way to determine size (in MB/KB, not the dimension of the image ) of image loaded into qpixmap. I'm using right now qpixmap.load(path) method, I know that I can load file into ByteArray and use qpixmap.loadFromData but wouldn't that be performance overkill?
Also is there a faster way of scaling image then qpixmap.scaled ? Right now I use combination of i.e. scaled( 640, 480, FastTransformation).scale(200, 100, SmoothTransformation). I know that I'm doing 2 sale but that's only for quality purpouse.

Best regards

wysota
3rd April 2010, 10:25
Hello,
I want to ask if there is a easy way to determine size (in MB/KB, not the dimension of the image ) of image loaded into qpixmap. I'm using right now qpixmap.load(path) method, I know that I can load file into ByteArray and use qpixmap.loadFromData but wouldn't that be performance overkill?

QFileInfo::size()


Also is there a faster way of scaling image then qpixmap.scaled?
No. You can only prepare the image in the size you need in advance but you will still be calling scaled().


Right now I use combination of i.e. scaled( 640, 480, FastTransformation).scale(200, 100, SmoothTransformation). I know that I'm doing 2 sale but that's only for quality purpouse.
That's quite an optimal combination. Provided the first transformation reduces the size by integer number of times compared to the original image.

Talei
3rd April 2010, 10:55
As always thank You very much for reply.
Another question is how to determine size of qpixmap after scaling? In example I want to import -> scale -> display size information -> save to file scaled image.

Also I'm using list/table View to display thumbnails so I prescale every Image, I know that I can retrieve information from views about what items are displayed, so I thought that I would scale only Images that are currently displayed and when user scroll automatic scale rest of them. Any suggestion how to proceed with this are more then welcome.

wysota
3rd April 2010, 11:32
Another question is how to determine size of qpixmap after scaling? In example I want to import -> scale -> display size information -> save to file scaled image.
Size of the pixmap is the width multiplied by height multiplied by number of channels multiplied by number of bits per gun. The file size you save it to will depend on the file format you will use and is practically impossible to determine upfront (you can only estimate on a case by case basis).


Also I'm using list/table View to display thumbnails so I prescale every Image, I know that I can retrieve information from views about what items are displayed, so I thought that I would scale only Images that are currently displayed and when user scroll automatic scale rest of them. Any suggestion how to proceed with this are more then welcome.
That's not a very practical approach when using list view. The list would have to relayout itself all the time and this is a bit time consuming. If you want such thing, it's better to subclass QAbstractScrollArea and implement the functionality as a regular custom widget.

Talei
4th April 2010, 07:25
Size of the pixmap is the width multiplied by height multiplied by number of channels multiplied by number of bits per gun. The file size you save it to will depend on the file format you will use and is practically impossible to determine upfront (you can only estimate on a case by case basis).
I'm familiar with compression, and I asked because I thought I could do something like compress it in memory, display size information depending on the compression level, and if user confirms save it (simply copy compressed data) on hdd. I actually didn't looked this subject in qt DOCS so I know I need RTM :).


That's not a very practical approach when using list view. The list would have to relayout itself all the time and this is a bit time consuming. If you want such thing, it's better to subclass QAbstractScrollArea and implement the functionality as a regular custom widget.
I tested it and not only relayouting takes time, but in my current implementation scaling (or actually rescaling again images). Currently I pre scale each thumbnails on load, so it takes time, but after this response of list/table views are extremely fast (I use QStandardItemModel for model and fetch data in views). Is it good idea to use delegates to scale items currently on view? I don't keep image data in model, only path to images, so I could load and scale them, that way memory usage won't be huge, but probably this will trigger relayouting of views like you wrote.
Setting setGridSize could prevent relayouting (of course when view is invisible) but I don't know if data change in model for DecorationRole role could trigger relayouting.

wysota
4th April 2010, 07:37
I'm familiar with compression, and I asked because I thought I could do something like compress it in memory, display size information depending on the compression level, and if user confirms save it (simply copy compressed data) on hdd. I actually didn't looked this subject in qt DOCS so I know I need RTM :).
Sure you can. Just save the image to a byte array. Then check its size and if user confirms the save, just save the byte array to disk without going through QImage or QPixmap again.


I tested it and not only relayouting takes time, but in my current implementation scaling (or actually rescaling again images).
Well... scaling has to take time, relayouting doesn't.


Currently I pre scale each thumbnails on load, so it takes time, but after this response of list/table views are extremely fast (I use QStandardItemModel for model and fetch data in views). Is it good idea to use delegates to scale items currently on view? I don't keep image data in model, only path to images, so I could load and scale them, that way memory usage won't be huge, but probably this will trigger relayouting of views like you wrote.

You can do that in another thread.


Setting setGridSize could prevent relayouting (of course when view is invisible) but I don't know if data change in model for DecorationRole role could trigger relayouting.
But then what's the point of scaling if you already have the grid size determined.

All in all I suggest you read this article: http://doc.trolltech.com/qq/qq27-responsive-guis.html. Especially the part about parallel programming.

Talei
4th April 2010, 08:23
I'm also familiar with threads (implemented it for qsocket, with signal/slot mechanism for progressbar) so I thought about that, especially that currently GUI freeze on load operation. (and you article is in my bookmarks :), good read )

But then what's the point of scaling if you already have the grid size determined.
That's right, I would need to change gridsize depending on the thumbnail scale so view would repaint also.
Maybe my current approach is wrong, because I use QPixmap to load images and from what I read QPixmap is fast on display but slow on pixel manipulation, so loading it as QImage then converting to QPixmap after scale could speed thing up?

wysota
4th April 2010, 08:29
Maybe my current approach is wrong, because I use QPixmap to load images and from what I read QPixmap is fast on display but slow on pixel manipulation, so loading it as QImage then converting to QPixmap after scale could speed thing up?
I don't think it would make a big difference if at all.

Talei
4th April 2010, 08:47
I don't think it would make a big difference if at all.
I tested it right now, no difference...
Also testing without scale don't speed loading, so only conclusion is that load is slow? I use

QPixmap tmp;
QStringList fileNames; //populated with i.e. 7 images, total file size 1.16MB, WinXP
for( int i = 0; i < fileNames.length(); ++i)
{
tmp.load( fileNames[i] );
model.setData( model.index( i, 0 ), tmp, Qt::DecorationRole );
}
to do that. (forcing extension don't speed up loading). Load is around 500 ms - 1sec.
After some testing
load files no scale debug mode 600-700mx
load files no scale release mode 300-360mx
load files with scale release mode 360-375mx
so scaling takes around 30ms on my test files.
EDIT:
I tried also QFile; and read pixmap from bytearray same result.
Also reading 123 images, size 35 MB took (on release compilation) 11s, copying that amount data on win is practically instant.

Talei
4th April 2010, 13:40
Sorry for double post, but that way its more readable.

Here chaos starts ( or, that's more likely, my lack of good programming skills )
After some more test, each test was performed after previous, on same set of data:
WinXP, total image size 35MB
Loaded images: 123 time: 10718 ms : 10 s
Loaded images: 16 time: 1344 ms : 1 s
Loaded images: 123 time: 10688 ms : 10 s
Loaded images: 123 time: 10671 ms : 10 s
Mem usage 47MB after 3 test.

Win7
Loaded images: 123 time: 1422 ms : 1 s
Loaded images: 123 time: 250 ms : 0 s
Loaded images: 123 time: 266 ms : 0 s
Loaded images: 123 time: 234 ms : 0 s
At this point I thought that drivers for my mobo on winXp could be at fault, after reinstall same results.

Xubuntu 64bit laptop C2D 1.8GHz, 5400 rpm hdd
qt 2010.02
Loaded images: 123 time: 37989 ms : 37 s
Loaded images: 123 time: 25553 ms : 25 s
Loaded images: 123 time: 41742 ms : 41 s
Mem usage after 3 test 1GB !!! Reinstall qt, retest:
Loaded images: 123 time: 30946 ms : 30 s
Mem usage, app starts 4MB, 358 MB

What I did: create QStandardItemModel, clear it at the beginning model.clear(), add in above loop QPixmap to column (in test there was 8 columns, as fallows qpixmap, qstring - path to img plus filename, qstring - width x height, qstring - file size, int width, int height, qstring - filename, qpixmap scaled to 320x240) ), after that output time.
Can someone explain to me what is going on?
Code that I tested:


void MainWindow::populateModel( QStringList fileNames, int sizeW, int sizeH )
{
listView->setIconSize( QSize( sizeW+10, sizeH+10 ) );
tableView->setIconSize( QSize( 100, 75 ) );

QStringList rowsNumbers;

QPixmap thumb, tmp;
QFile imgRead;

model.setRowCount( fileNames.length() );
model.setColumnCount( 8 );
model.setHorizontalHeaderLabels( QStringList() << "Miniaturka" << "Ścieżka" << "Wymiar zdjęcia" << "Rozmiar pliku" );

for( int i = 0; i < fileNames.length(); ++i)
{
imgRead.setFileName( fileNames[i] );
imgRead.open( QIODevice::ReadOnly );
tmp.loadFromData( imgRead.readAll() );
imgRead.close();

if( tmp.width() > sizeW || tmp.height() > sizeH ){
//for speed first fast scalle then smooth
thumb = tmp.scaled( sizeW, sizeH ).scaled( sizeW, sizeH, Qt::KeepAspectRatio, Qt::SmoothTransformation );
}else{
thumb = tmp.scaled( 640, 480).scaled( sizeW, sizeH, Qt::KeepAspectRatio, Qt::SmoothTransformation );
}

rowsNumbers.append( QVariant(i+1).toString() );

tableView->setRowHeight( i, 75 );
tableView->setColumnWidth( 0, 100 );

QFileInfo fi( fileNames[i] );

model.setData( model.index( i, 0 ), thumb, Qt::DecorationRole );
model.setData( model.index( i, 1 ), fileNames[i], Qt::DisplayRole );
model.setData( model.index( i, 2 ), QVariant( tmp.width() ).toString() + "X" + QVariant( tmp.height() ).toString()
, Qt::DisplayRole );

model.setData( model.index( i, 3 ), QVariant( fi.size()/1024 ).toString() + " KB", Qt::DisplayRole );

model.setData( model.index( i, 4 ), QVariant(tmp.width()).toString(), Qt::DisplayRole );
model.setData( model.index( i, 5 ), QVariant(tmp.height()).toString(), Qt::DisplayRole );
model.setData( model.index( i, 6 ), fi.fileName(), Qt::DisplayRole );
model.setData( model.index( i, 7 ), thumb, Qt::DecorationRole );

}
}

EDIT: I didn't add model to view in test.
EDIT: I think that tmp.loadFromData is at fault here. Maybe some issue when copying data?
EDIT: Problem with JPG import, importing same files only uncompressed BMP (400 MB) took 4sec!!!! PNG - 100MB - 10s, recompressed JPG - 11MB - 11s.
EDIT. I also used ifstream to load data, data loads in 1-2sec, time going up when called method pixmpa.loadFromData (11s)

wysota
4th April 2010, 19:33
Also reading 123 images, size 35 MB took (on release compilation) 11s, copying that amount data on win is practically instant.

Opening an image and copying a file are two different things. Especially if you test copying the file right after opening an image from it as the file resides either in the disk cache or in system buffers so no read needs to actually take place.


EDIT: Problem with JPG import, importing same files only uncompressed BMP (400 MB) took 4sec!!!! PNG - 100MB - 10s, recompressed JPG - 11MB - 11s.

35MB worth of jpeg is an enormous volume (JPG compression is typically a factor of 10 so it's 350MB of raster data). This just has to take time. BMP loads faster because pixel data is immediately available in BMP. JPEG encodes images more as signal frequencies than actual bitmaps so it just has to take time to restore. If you are not satisfied with the speed, why don't you load the images in a second thread as I already suggested?

Talei
5th April 2010, 08:38
Opening an image and copying a file are two different things. Especially if you test copying the file right after opening an image from it as the file resides either in the disk cache or in system buffers so no read needs to actually take place.
Test were done after restart of OS. I tested only loading data into bytearray, and next also populating model. From test I can tell that qpixmap.loadformdata( bytearray ) took long time. Load data was fast as scaling. Also WinXP use, if I remember correctly, prefetch and is not so efficient as superprefech in Win7/Vista. From test you can see that what you are talking about takes place on Win7, first load takes more time then second.

35MB worth of jpeg is an enormous volume (JPG compression is typically a factor of 10 so it's 350MB of raster data). This just has to take time. BMP loads faster because pixel data is immediately available in BMP. JPEG encodes images more as signal frequencies than actual bitmaps so it just has to take time to restore. If you are not satisfied with the speed, why don't you load the images in a second thread as I already suggested?
I highly doubt that reversing wavelet for jpeg200 or dct can take so long (I don't count zig-zag, huffman, crc, etc.. because they are from algorithm complexity point much lighter the dct/wavelet ).
I didn't test threads because I use 1CORE (AMD Athlon 64 SanDiego 2.2GHz/3700+) and from my experience (practical and theoretical) threads don't speed processing (when CPU usage is 100%) only unfreeze GUI (tested on ZBrush/3ds max, etc...).
But I implemented thread, and... everything works damn FAST! I don't know what is happening, can you explain to me why executing function posted before on separate thread fix the problem? (is it that GUI queue slows dawn this function?).

EDIT: Am I correctly assuming that memory problem on Linux is due to:
Linux - qpixmap keep uncompresed data.
Win - qpixmap keeps compressed version in memory, then on display decompress it on the fly (or gdi kick in action?)?