0

I am doing a gamma correction on an image of dimensions 5000x3000x3.

The formula is

value ^ (1 / gamma)

for RGB values from 0.0 to 1.0

My input gamma values range from 0.0 to 10.0 while gamma = 0.0 always outputs 0.0.

The trouble is that the involved pow computation is so slow.

Doing this takes about 1300 milliseconds on a float[, ,]:

for (int y = 0; y < 3000; y++)
{
    for (int x = 0; x < 5000; x++)
    {
        for (int z = 0; z < 3; z++)
        {
            arr[x, y, z] = (float)Math.Pow(arr[x, y, z], 0.3);
        }
    }
}

And using NMathFunctions.Pow on a FloatMatrix this takes about 1100 milliseconds:

a = NMathFunctions.Pow(a, 0.3f);

Any idea how to speed things up?

tde
  • 150
  • 13
  • Depending on your accuracy requirements you can always set up a look-up table and use nearest-neighbor or linear interpolation (or higher-order interpolation, but that will most likely not solve the performance problem). – Kristof U. Feb 21 '18 at 10:40
  • Have you thought of doing adding parallelization? Seems to me every value is independent of the rest, so there's no reason a parallel algorithm would not work – Glubus Feb 21 '18 at 10:48
  • Indeed I did quite a bit of googling before I asked but I was hoping for something a little simpler that works in C# right away. Thanks for the link, I will go through it. I am not a mathemagician but help me understand how 2^(p*log2(x)) simplifies anything? There is still a pow operation involved? Edit: Why did you delete your comment? – tde Feb 21 '18 at 10:51
  • @Glubus That is worth a try yes. I was just assuming that a library like NMath would take care of that. – tde Feb 21 '18 at 10:53
  • @tde, I've deleted comment, because my math skills aren't good anymore to prove it (and I can't edit comment after 5 min). The idea is to represent expensive operation as expression consisting from cheap ones, now your turn to google which ones ;) – Sinatr Feb 21 '18 at 10:53
  • @Sinatr Thanks! :) – tde Feb 21 '18 at 10:56
  • Pow might be slow but accessing the array 90 million times could be a factor as well. You could try some unsafe code an use pointers to update the array to reduce bounds checks... – Emond Feb 21 '18 at 11:16

1 Answers1

1

EDIT: Actually I'm not sure this works will if the power is smaller than 1. I know it works for gamma correction when you want to replace pow(x, 2.2). it works even better when the power is higher, but might not be good with powers smaller than 1


Indeed, pow is a really slow function, even slower than sqrt (Which make sense since any sqrt operation could be done with pow using a fraction as exponent)

There is no way to compute the power exactly more efficiently, but there are pretty good way to estimate it.

One condition is that your base value is in the range [0, 1], which is true in your case. Then you can get a good estimate (accurate at around 99%). here is how you can do it:

Use a mix of discrete powers, that you compute yourself instead of with pow. so for example with x in the range [0, 1],

instead of

result = pow(x, 2.2)

do

result = 0.8*x*x + 0.2*x*x*x

Note that it is perfectly accurate when x is 0 and when x is 1 (results are 0 and 1 respectively)

I hope this will work for you

Basile Perrenoud
  • 4,039
  • 3
  • 29
  • 52
  • 1
    The OP needs `0.3` power, can you provide an explanation how `2.2` become equation with `0.8` and `0.2` as well as some multiplication? – Sinatr Feb 21 '18 at 11:00
  • It's incredibly frustrating that this isn't explained any further. – T. J. Evers Sep 02 '23 at 11:33