I try to compute the lightness of an RGB pixel with various methods, in C99 with GCC 9. I have this enum :
typedef enum dt_iop_toneequalizer_method_t
{
DT_TONEEQ_MEAN = 0,
DT_TONEEQ_LIGHTNESS,
DT_TONEEQ_VALUE
} dt_iop_toneequalizer_method_t;
which is user input from a GUI.
Then, I have several functions corresponding to each case:
typedef float rgb_pixel[4] __attribute__((aligned(16)));
#pragma omp declare simd aligned(pixel:64)
static float _RGB_mean(const rgb_pixel pixel)
{
return (pixel[0] + pixel[1] + pixel[2] + pixel[3]) / 3.0f;
}
#pragma omp declare simd aligned(pixel:16)
static float _RGB_value(const rgb_pixel pixel)
{
return fmaxf(fmaxf(pixel[0], pixel[1]), pixel[2]);
}
#pragma omp declare simd aligned(pixel:16)
static float _RGB_lightness(const rgb_pixel pixel)
{
const float max_rgb = _RGB_value(pixel);
const float min_rgb = fminf(pixel[0], fminf(pixel[1], pixel[2]));
return (max_rgb + min_rgb) / 2.0f;
}
Then, the loop over the image is:
static void exposure_mask(const float *const restrict in,
float *const restrict out,
const size_t width,
const size_t height,
const dt_iop_toneequalizer_method_t method)
{
#pragma omp parallel for simd default(none) schedule(static) aligned(in, out:64)
for(size_t k = 0; k < 4 * width * height; k += 4)
{
const rgb_pixel pixel = { in[k], in[k + 1], in[k + 2], 0.0f };
out[k / 4] = RGB_light(pixel, method);
}
}
My first approach was to use an RGB_light()
function with a switch/case mapping the method
and the functions, but that triggers checks for every pixel, which is quite expensive.
My idea would be to use a list or a struct
of the methods, like that:
typedef struct RGB_light
{
// Pixel intensity (method == DT_TONEEQ_MEAN)
float (*_RGB_mean)(rgb_pixel pixel);
// Pixel HSL lightness (method == DT_TONEEQ_LIGHTNESS)
float (*_RGB_lightness)(rgb_pixel pixel);
// Pixel HSV value (method == DT_TONEEQ_VALUE)
float (*_RGB_value)(rgb_pixel pixel);
} RGB_light;
then initiatize the method once for all before the loop, like
static void exposure_mask(const float *const restrict in,
float *const restrict out,
const size_t width,
const size_t height,
const dt_iop_toneequalizer_method_t method)
{
lightness_method = RGB_light[method]; // obviously wrong syntax
#pragma omp parallel for simd default(none) schedule(static) aligned(in, out:64)
for(size_t k = 0; k < 4 * width * height; k += 4)
{
const rgb_pixel pixel = { in[k], in[k + 1], in[k + 2], 0.0f };
out[k / 4] = lightness_method(pixel);
}
}
but I did not succeed in translating that idea to actual working code.
There is something similar to what I want to do in Python :
def RGB_value(pixel):
return whatever
def RGB_lightness(pixel):
return whatever
methods = { 1: RGB_value, 2: RGB_lightness }
def loop(image, method):
for pixel in image:
lightness = methods[method](pixel)