PDA

View Full Version : Re: How to effiecently copy individual pixels (with QImage)



KillerBerry
17th July 2015, 20:56
I have lately been experiencing some performance issues when using the pixel() and setPixel() functions of the QImage class.
Basically this is the code that caused all the issues.

frame->setPixel(x,y, tex.pixel(pixelCoord));
frame as well as tex are of type QImage and QImage* respectively. They both are in the Format RGB32.
This function will be called up to a few million times a second.
All I want to do is to copy the colour of one pixel from one image into the other.
Does anybody know why copying pixels like that is extremely slow (shouldn't it be just as quick as setting any other variable) and how I can speed up my code? (Preferrably but not neccessarily using QImage)

jefftee
17th July 2015, 23:27
Haven't done any direct image manipulation myself with Qt, but if you're processing every bit of the image one bit at a time, have you looked at processing by scan line using QImage::scanLine() and QImage::bits()?

ChrisW67
18th July 2015, 01:10
This function will be called up to a few million times a second.

Really? Are you iterating over entire frames of video in real-time?


All I want to do is to copy the colour of one pixel from one image into the other.

If you are iterating over the entire QImage pixel-by-pixel then you are far better off copying the QImage.


*frame = tex;

If you are, for example, inserting a smaller tex image into a position in the frame then you could do that with QPainter (which can resize and subset the images for you also):


#include <QApplication>
#include <QImage>
#include <QPainter>
#include <QTime>
#include <QDebug>

int main(int argc, char **argv)
{
QApplication app(argc, argv);

QImage frame(1024, 768, QImage::Format_RGB32);
frame.fill(Qt::white);

QImage tex(256, 256, QImage::Format_RGB32);
tex.fill(Qt::green);

QPainter p;
QTime timer;
timer.start();
for (int i = 0; i < 1000000; ++i) {
p.begin(&frame);
p.drawImage(QRectF(100, 100, 128, 128), tex, QRectF(128, 128, 128, 128), Qt::AutoColor);
p.end();
}
// Copied 128x128x1000000 == 16,384,000,000 pixels, about 6500 milliseconds on my machine
qDebug() << "Done in" << timer.elapsed() << "msec";

frame.save("frame.png");

return 0;
}

KillerBerry
18th July 2015, 17:34
The line of code is part of an algorithm I wrote which performs a Z-Buffer operation and afterwards perspective correct texture mapping. (Without OpenGL, that's the entire point of the project)
What I basically do is to iterate over parts of the frame and calculate texture coordinates. I then want to grab the pixel from the texture at said coordinate and copy it into my frame buffer.
I have found a solution using some funky pointer arithmetic, however this only works well if I keep the format at RGB32. Is there any standard way to efficiently copy pixels and why are the methods in QImage so slow? In the Qt documentation it says something about setPixel() but not pixel().

anda_skoa
18th July 2015, 20:58
pixel() and setPixel() need at least to calculate the offset based on the two coordinates.
Direct access via bits() or scanline() doesn't.

It could additionally be the case that the compiler can optimize the array access (which using bits() or scanline() effectively is) better than it can access a nested for loop (which you likely have when using setPixel/pixel).

But you could easily benchmark these guesses:
- create an image of a specific size
- iterate over it using pixel/setPixel to add a constant number to each pixel value. measure the time
- do the same thing using a single for loop and bits(). also measure time.

Maybe write it using QTestLib and its Q_BENCHMARK feature to get even better statistics.

Cheers,
_