1

I am currently evaluating if Halide is a good choice for my programmes. As a short Hello Halide example, I wanted to convert an rgb image to hsl space. However, when trying, I got stuck at the first stage.

In order to convert to hsl, I would need to calculate the chroma image first. This is the difference between the maximum and minimum channel value for a given pixel. I tried to find something similar in the documentation, but I was not able to. How would I do this in Halide?

In C++ it would be something like:

for (auto y = 0; y < image.height(); ++y)
    for (auto x = 0; x < image.width(); ++x)
    {
        auto maximum = 0;
        auto minimum = 255;
        for (auto c = 0; c < image.channels(); ++c)
        {
            if (image(x, y, c) < minimum)
                minimum = image(x, y, c);
            if (image(x, y, c) > maximum)
                maximum = image(x, y, c);
        }
        chroma(x, y) = maximum - minimum;
    }
jan.sende
  • 750
  • 6
  • 23

1 Answers1

1

Here's an example Halide program for converting from RGB to HSL:

#include <Halide.h>
#include <halide_image_io.h>
using namespace Halide;
using namespace Halide::Tools;

int main(int argc, char *argv[]) {
    Buffer<float> input = load_and_convert_image(argv[1]);

    Func max_channels, min_channels, chroma, result_hsl;
    Var x, y, c;

    Expr R = input(x, y, 0);
    Expr G = input(x, y, 1);
    Expr B = input(x, y, 2);

    max_channels(x, y) = max(R, G, B);
    min_channels(x, y) = min(R, G, B);
    chroma(x, y) = max_channels(x, y) - min_channels(x, y);

    Expr V = max_channels(x, y);
    Expr C = chroma(x, y);

    Expr H = select(C == 0, 0,
                    R == V, 60 * (0 + (G - B) / C),
                    G == V, 60 * (2 + (B - R) / C),
                    60 * (4 + (R - G) / C)) /
             360;
    Expr L = V - C / 2;
    Expr S = (V - L) / min(L, 1 - L);

    result_hsl(x, y, c) = select(
            c == 0, H,
            c == 1, S,
            L);

    result_hsl
            .bound(c, 0, 3)
            .reorder(c, x, y)
            .unroll(c, 3);

    Buffer<float> result = result_hsl.realize({input.width(), input.height(), 3});

    convert_and_save_image(result, argv[2]);
}

and here's the CMakeLists.txt I used to build this:

cmake_minimum_required(VERSION 3.20)
project(rgb_to_hsl)

find_package(Halide REQUIRED)

add_executable(rgb_to_hsl main.cpp)
target_link_libraries(rgb_to_hsl PRIVATE Halide::Halide Halide::ImageIO)

There might be bugs with the actual HSL conversion, here, but it should still demonstrate the big ideas. Note one counterintuitive thing here: we bound() and unroll() the channel dimension (after reorder()-ing it innermost) to avoid any loop / branching overhead for that math.

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • Is there any way to write this without explicitly defining `input(x, y, 0)`, ... If possible, I would like to have this as another loop variable. – jan.sende Aug 10 '21 at 00:45
  • @jan.sende I'm not sure what you're talking about. I don't define that explicitly (i.e. as an update definition) – Alex Reinking Aug 10 '21 at 04:37
  • I'm sorry, I'm not very good at expressing my question. At the moment you are selecting the color channel by manually inserting a `0`. Is it possible to do this with a variable, like `x` and `y`? That is to say, can I somehow calculate `max_channel` genericly, without assuming a three channel image? (I would like to know how to calculate `max_channel` on an *n*-channel image, where *n* is only known at runtime.) – jan.sende Aug 10 '21 at 05:10
  • In that case, you'll have to use a reduction domain, but that doesn't make sense for rgb -> hsl, which assumes 3-channel... – Alex Reinking Aug 10 '21 at 05:45
  • I see. Could you maybe add an explanation on these reduction domains? I will amend the question. – jan.sende Aug 16 '21 at 12:28
  • @jan.sende - I am happy to answer, but I think it would be more useful to ask a new question about reduction domains, rather than amending this one. – Alex Reinking Aug 16 '21 at 12:31
  • Damn. I already made the change. Let me know if you think the current form works, or if I should roll this back and put it as a new question. – jan.sende Aug 16 '21 at 12:37
  • Here's the deal. I put a good bit of time into answering your "RGB->HSL" question. Editing your question invalidates my answer, and makes this whole exchange less useful for others. Asking a new question about what you really want to know lets me write a different, more focused answer that might help other readers down the line. – Alex Reinking Aug 16 '21 at 12:41
  • 1
    Okay. Reverted the question back and split the multi-channel part into a separate one. – jan.sende Aug 17 '21 at 12:08
  • @jan.sende - Great! Do you have a link to the other question? I couldn't find it under the `halide` tag. – Alex Reinking Aug 17 '21 at 18:01
  • Sorry. Others pointed out some serious errors in my example yesterday, so I deleted it. I just now posted an [improved version](https://stackoverflow.com/questions/68832787/how-to-calculate-maximum-minimum-over-n-channels-in-halide-using-domains). – jan.sende Aug 18 '21 at 12:56