Question

I am currently working on generating "heat-maps" with QPainter and QImage. My method consists of drawing multiple circles with black to transparent QRadialGradients as the QBrush (see "Intensity Map"). Then I apply a gradient map to the intensity map to get the desired "heat-map" effect (see "After Gradient Map").

The issue I am having, which is more apparent in the "After Gradient Map" image, is that the circles are not blending correctly. Where circles overlap do seem to partially blend, but towards the edges you can clearly see where the circles end (almost a outer-glow). I would like an effect which has no visible borders between the circles and blends correctly.

Intensity Map

Intensity Map

After Gradient Map (different intensity map)

After Gradient Map

Code

// Setup QImage and QPainter
QImage *map = new QImage(500, 500, QImage::Format_ARGB32);
map->fill(QColor(255, 255, 255, 255));
QPainter paint(map);
paint.setRenderHint(QPainter::HighQualityAntialiasing);

// Create Intensity map
std::vector<int> record = disp_data[idx]; // Data
for(int j = 1, c = record.size(); j < c; ++j) {
    int dm = 150 + record[j] * 100 / 255; // Vary the diameter

    QPen g_pen(QColor(0, 0, 0, 0));
    g_pen.setWidth(0);
    QRadialGradient grad(sensors[j-1].x, sensors[j-1].y, dm/2); // Create Gradient
    grad.setColorAt(0, QColor(0, 0, 0, record[j])); // Black, varying alpha
    grad.setColorAt(1, QColor(0, 0, 0, 0)); // Black, completely transparent
    QBrush g_brush(grad); // Gradient QBrush
    paint.setPen(g_pen);
    paint.setBrush(g_brush);
    paint.drawEllipse(sensors[j-1].x-dm/2, sensors[j-1].y-dm/2, dm, dm); // Draw circle
}

// Convert to heat map
for(int i = 0; i < 500; ++i) {
    for(int j = 0; j < 500; ++j) {
        int b = qGray(map->pixel(i, j));
        map->setPixel(i, j, grad_map->pixel(b, 0)); //grad_map is a QImage gradient map
    }
}

As you can see, there is no QPen for the circles. I have been trying a variety of blending modes with no success. I have also changed the rendering hint to HighQualityAntialiasing. I have also tried making the circles much larger than the radial gradient, so there is no way the gradient is cut-off or a border is applied to the outside of the circle.

Any ideas? Thanks!

Was it helpful?

Solution

I think this is a form of mach-banding, which is an optical illusion where changes in luminance are enhanced by the visual system, causing the appearance of bright or dark bands which are not actually present in the image. Typically these are seen on the boundary between two distinct areas, but in the case here I believe it is the sharp discontinuity in the gradients being observed.

Here are some images to demonstrate the issue:

This first image is calculated in software, and consists of three circles each drawn with a radial linear gradient. Mach-band effects should be visible at the edges of the overlap between circles, as these are the points where the gradient sharply changes.

Grayscale, banded

This second image is exactly the same calculation, but instead of being linear along the radius, the gradient is mapped to a curve (I used the first hermite basis function). The bands should almost entirely have disappeared:

Grayscale, smooth

As to why this affects a colourised image more, I'm not sure it does. I think in the case above, perhaps there is additional banding caused by the colourisation effectively being a palette lookup, resulting in additional banding.

I performed roughly the same colourisation locally, also simply mapping a palette, and the effect is similar:

enter image description here enter image description here

Fixing this using QT's linear gradients is probably non-trivial (you could try adding a lot more control points to the gradient, but you'll have to add quite a few...), but calculating such an image in software is not hard. You could also consider some other post-processing effects, such as adding a blur, and/or adding noise. Anything breaking the discontinuity in the gradient would likely help.

OTHER TIPS

I agree with JasonD.

Furthermore, please keep in mind that Qt is doing linear blending in sRGB color space, which is not linear (it has a gamma 2.2 applied).

To do this right, you need to do the blending or interpolation in linear light, then convert to sRGB (apply gamma) for display.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top