How to remap data using a custom palette using OpenGL

Viewed 253

Let's say I have data matrix 640x256 where each element is a float number within a range from -10 to 600. I want to display this matrix on the screen where each element gets assigned a color from RGB palette. OpenGL, C++ on RaspberryPi

What I currently do:

    for (unsigned int i = 0; i < frameSize; ++i) { // frameSize = 640x256
        float value = data[i];

        if (value > maxVal) {
            color = palette.back();
        }
        else if (value < minVal) {
            color = palette.front();
        }
        else {
            color = palette[((value - minVal) / (maxVal - minVal)) * (palette.size() - 1)];
        }
        imgData[3 * i] = color.R;
        imgData[3 * i + 1] = color.G;
        imgData[3 * i + 2] = color.B;
    }

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgData);
    glBegin(GL_QUADS);
    {
        glTexCoord2f(0, 0); glVertex2f(0, 0);
        glTexCoord2f(1, 0); glVertex2f(width, 0);
        glTexCoord2f(1, 1); glVertex2f(width, height);
        glTexCoord2f(0, 1); glVertex2f(0, height);
    }
    glEnd();

palette is user-defined vector of RGB colors; minVal and maxVal are minimum and maximum value in data, but can be changed by user.

This code works correctly but image is colorized on CPU which can be slow for large data. Is it possible to pass float data as texture directly and colorize it with the palette using OpenGL on the output?

I am not asking how to optimize CPU-bounded loop. My question is if it is possible to completely remove it and do its task during OpenGL rendering.

2 Answers

have you measured how much time is taking what (so where is it slow)?

  • the rendering on GL
  • transfer of texture between CPU and GPU
  • recoloring

my bet is the recoloring... Here few hints what I would change:

  1. you are doing 3x byte access

     imgData[3 * i    ] = color.R;
     imgData[3 * i + 1] = color.G;
     imgData[3 * i + 2] = color.B;
    

    it would be better to align pixelformat to 32 bit (assuming your CPU is 32 bit with 32 bit memory access, even 16 bit access will improve speed more than twice if supported by HW) and do this instead:

     imgData[i] = color;
    

    where color is 32 bit unsigned int.

  2. you are doing too many "slow" operations

    I do not code for RasberryPi but I suspect your CPU is not as advanced as x86/x64 these days so there is most likely still huge performance difference between +,-,*,/ and int/float operations. So for example:

    3 * i + 2
    ((value - minVal) / (maxVal - minVal)) * (palette.size() - 1)
    

    can be quite slow luckily they can be done differently for example:

    unsigned int frameSize3 = frameSize3*3,i;
    float value,dv=float(palette.size()-1)/(maxVal - minVal); 
    for (i=0;i<frameSize3;)
      {
      value = data[i];
           if (value>maxVal) color=palette.back(); // here you got temp is it typo?
      else if (value<minVal) color=palette.front(); // here you got temp is it typo?
      else color = palette[int((value-minVal)*dv)];         
      imgData[i]=color.R; i++;
      imgData[i]=color.G; i++;
      imgData[i]=color.B; i++;
      }
    

    still your main problem is the conversion between float and int that is slow ... so is data[] really floating or integer? What is the value,range of data[i],maxVal,minVal,palette.size() ? are they floating or integer?

  3. use LUT for color computation

    in case you can convert to integer (depends on the range of your values you claim -10...600 that OK but what is the palette size and maxVal,minVal? ) then:

    color = palette[((value - minVal) / (maxVal - minVal)) * (palette.size() - 1)];
    

    can be done like this:

    color = LUT[value - minVal];
    

    where LUT[610] is color for each value taken from the palette. You can pre-compute it once on app start or after any change to palette,minVal,maxVal.

    LUT[i] = (i*(palette.size()-1)) / (maxVal-minVal);
    

    in case your input data is from camera try to look if it can return integer data instead (even fixed format) that would speed up a lot ...

I'll just put this as an answer because a comment would be too long.

I don't think OpenGL is the way to go for what you want to accomplish. Getting data in and out of shaders using uniforms is not that straightforward, especially since you'd have to kinda re-learn OpenGL by moving from 1.x to 3.x, as @Scheff mentioned (although I'd recommend the move from a 20+ year old library).

I'd suggest one of the following approaches:

1. Parallelizing on CPU using a pool of std::threads: Since the calculations are independent, you could divide the the image into multiple sections that are separately filled. Synchronization is gonna be on you though. std::async might help you there.

2. Parallelizing on CPU/GPU using OpenMP: Using OpenMP is relatively straightforward and it's supported by most modern compilers. Since version 4.0, it'll also let you utilize the GPU for your parallel computation.

OpenMP probably has the best trade-off between implementation time and performance gain.

Related