PDA

View Full Version : Painting of lots of computationally heavy pixels



Cruz
11th April 2010, 16:02
Hello!

I'm looking for techniques to speed up the painting of a lot of pixels, each of which requires up to a 1 millisecond to calculate. What I have so far is that I precalculate the colors of a 640x480 patch and then draw it pixel by pixel in paintEvent() using drawPoint(). The pure drawing takes several seconds, which is way too long. Is there something I can do to speed this up? Something like painting into an off screen pixmap and then showing that somehow?

Soon I will have to be able to draw lines and curves on top of this 640x480 patch, which frequently change. How can I achieve that only the foreground is repainted and the patch stays in the background and doesn't need to be repainted at all?

Thanks
Cruz

JohannesMunk
11th April 2010, 16:22
1. You should create an in-Memory-image with your pixels! That can be drawn much faster. See QImage and QPixmap documentation.

2. If you are calculating those values you will probably have some valueToColor function. Usually it is much faster to precalculate once a LUT (Look-Up-Table) with the required valueresolution.

3. Describe what you want to do. It's hard to give good advice when you have to guess what its for.

HIH

Johannes

Cruz
11th April 2010, 16:31
Thanks for your reply. Yes, I am already precalculating the look up table while I entertain the user with a progress bar. I'm looking into QPixmap as I'm writing this. So far I don't know how to show it on the screen but probably soon. :)

What I want to do is drawing something like a heat map on the screen. The color of every pixel needs to be calculated individually. The calculated colors are in this look up table. So all I need is an efficient way to show the heat map. On top of the heat map I need to draw lines that outline the borders between cold and hot regions. These lines have a bias towards cold or hot and this bias is manually configurable. So whenever the user changes the bias, the lines have to be redrawn immidiately and it cannot take several seconds. So either I find a way to redraw the heat map and the lines in no time, or I have to figure out how not to redraw the heat map, only the lines.

JohannesMunk
11th April 2010, 16:47
1. Easiest way to show a pixmap inside a GUI is a QLabel (http://doc.trolltech.com/latest/qlabel.html). See QLabel::setPixmap;
2. Rough layout would be:
a) once on program-start or when user changes the heat-colors: Calculate your Colour-LUT. Usually a linear interpolation between several colors: black, red, yellow for instance.
b) create the heatimage: create a QImage. iterate through every pixel. you could use scanline for most efficient memory access. calculate the heatvalue of this location. look the value up in the colour-lut. store the result in the image.
c) displayimage: Copy-Construct from the heatimage. Use a QPainter on that copy to draw your regions. assign displayimage to your qlabel. label.setPixmap(QPixmap::fromImage(img)).

This is going to be quite fast. But of course there are faster solutions. Like using a QGraphicsScene with OpenGL and have the heatimage stored as a texture. But I suggest you try the basic approach first.

Need help with details?

Johannes

Cruz
11th April 2010, 16:54
Wow thanks a lot, that just saved me several hours of tinkering! I think I can implement your instructions. I have a few questions though. Why do I use QImage and not QPixmap? And why is it better to set the QImage as the background of a QLabel instead of having QPainter drawPixmap()-ing it in the paintEvent()?

JohannesMunk
11th April 2010, 17:00
QImage allows faster per pixel manipulation/access (=> scanline). The final conversion to a Pixmap will use implicitly shared data if your color format is acceptable.

paintEvent of what Widget? You don't need to create a new subclass to just show an image. But if you intend to build your own heatmap widget with scales etc drawPixmap in paintEvent will do just fine/better!

BTW: I don't think you got the concept of a LUT yet.. For its creation, you won't need a progressbar! If you have a lot of pixels, lot's of them will have the same color, because they have the same value within a given resolution. The idea of a LUT is to do the following: color = LUT[qround(value)]; So the LUT is just an array of increasingly 'hotter' colors in your case.. Depending on the number of pixels, usage of a color-LUT can be a big time saver. If you say you want to differentiate 10k heat-values, you need to calculate the color values only for 10k values instead for 640x480 ~ 300k values.

Joh

Cruz
11th April 2010, 17:13
Yo, drawPixmap works like a charm. My widget draws in no time now and even resizing is not a problem. The heat map is in place. Well almost.
On top of the pixel by pixel heat map I draw little circles to mark special points, like peaks of mountains. The circles used to look really nice with painter.setRenderHint(QPainter::Antialiasing, true), but now they look really bad on the pixmap. If I turn the anti aliasing on, somehow my desktop wallpaper is mixed into the pixmap.

About the LUT, unfortunately I can't optimize it the way you suggest. There is no way around calculating the "heat" for every single pixel individually and that is what costs time. The linear interpolation between two colors to represent the heat value doesn't change anything compared to that.

JohannesMunk
11th April 2010, 17:20
If you use your own custom widget for the painting.. you won't need the displayimage, necessarily. Just drawPixmap the heatimage on the widgets painter. and then directly draw your circles. Does that work?

LUT: Well it adds up. And it depends on how often you need to refresh your heatimage. But it seems pretty stationary in your case. So forget about it. I had 2560x1600 realtime changing pixels where it made a BIG difference..

Cruz
11th April 2010, 17:30
Yes it does work. :)

Well, maybe after everything else is done I will tinker with the LUT and see how much it does. But I doubt it will be noticable. Compared to the 300k times 1 ms for each pixel of the heat map, reducing 300k linear interpolations to 10k look ups is most likely less than 0.1 percent of the total runtime.

Thanks for the help mate! We put this thing together in less than two hours.

JohannesMunk
11th April 2010, 17:35
Well it went that fast because you didn't need any help implementing. Good work!

BTW: If you want to do me a favour, use the forums thanks button, once :->

Bye!

Johannes