0

I want to calculate a bunch of pixels and then put them into a QImage using QtConcurrent::mappedReduced. But I get QImage::setPixel: coordinate (636,442) out of range error. That is presumably because of using the default QImage constructor which constructs a null image. I didn't find any way in the documentation how to set the constructor arguments or how to provide the initial value for the reduction. Is there any way how to do this? I thought that reduction requires you to specify the initial value... like in JS... but Qt probably had a different idea.

skeleton:

struct Pixel{
    QRgb value;
    QPoint pos;
};

void reducer(QImage &result, const Pixel &pixel){
    result.setPixel(pixel.pos,pixel.value);
}

I found a workaround... but this code is not optimal... because now i have to make a check every time the reducer runs...

void reducer(QImage &result, const Pixel &pixel, int width, int height){
    if(result.width()==0)
        result = QImage(width,height, QImage::Format_RGB888);
    result.setPixel(pixel.pos,pixel.value);
}
...
auto boundReducer = std::bind(reducer,_1,_2,width,height);
petoknm
  • 11
  • 6

1 Answers1

0

This check is very cheap, but your reducer does very little work, so you'll have performance problems no matter what.

To make this a bit cleaner, check if the image is default-constructed - i.e. null, and pass the intended initial image as a parameter, instead of passing width/height.

void reducer(QImage &result, const Pixel &pixel, const QImage& initial) {
    if (result.isNull()) 
      result = initial;
    result.setPixel(pixel.pos,pixel.value);
}

auto boundReducer = std::bind(reducer, _1, _2, 
                              QImage(width,height,QImage::Format_RGB888));

You could also use a class that derives from QImage and knows how to default-construct itself, but to pass runtime variable arguments to such a class would require the use of static member variables. So that's too much of a hack I think.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks :D Yeah... passing in the preconstructed image makes sense... but why do you think that there will be performance problems because of the simplicity of the reducer? – petoknm Jun 02 '16 at 18:13
  • @petoknm Because the `setPixel` operation takes much less time than the overhead of iterating the `pixel` container and sending out the pixels to the thread pool to process. The `setPixel` is a 32-bit write operation into memory. There's no point in parallelizing it, pretty much, because the fastest you can do it is at full memory bandwidth. This operation is memory bandwidth constrained, in other words. I/O constraints don't gain from parallelization. You could sort the pixel container along y,x then iterate and it'll be at the speed of RAM. – Kuba hasn't forgotten Monica Jun 02 '16 at 18:39
  • Thanks for the explanation – petoknm Jun 03 '16 at 16:03