1

I want to implement simple image processing routine quite similar to Auto Levels, so need to precalculate thresholds, make LUT and then make histogram stretching/normalization applying LUT.

But my question is not about algorithm side, it is about using extern defined functions, because i need a couple of while cycles for LUT calculation and i think using extern is good for it.

I tried following examples from Halide sources and checked this question too

I use AOT compilation currently testing on PC(winx64), aiming for arm in future, and have the following generator code:

    Var x("x"), y("y");
    Func make_a_root{ "make_a_root" };

    Buffer<bitType>  Lut{256, "lut"};

    make_a_root(x, y) = inputY(x, y);
    ExternFuncArgument arg = make_a_root;

    Func g;
    g.define_extern("generateAutoLevelsLut", { arg }, UInt(8), 2, Halide::NameMangling::CPlusPlus);

    g.compute_root();

inputY has Input<Buffer<uint8_t>> inputY{ "input_y", 2 }; type

First i just want to make it run the call, so function body makes nothing but print (can i define function in same cpp file as generator?)

int generateAutoLevelsLut(halide_buffer_t * input, halide_buffer_t * out)
{
    printf("\nextern call\n");
    return 0;
}

I tried default mangling with extern "C" too.

Never succeeded getting print message though, so my question is, why this happenin. Is it just misunderstanding on some syntax or are there any problem with calling extern function from generator code?

EDIT:

Added usage of extern like 'out(x,y) = g(x,y)' (lvalue should be actually used!) , and it started to make a call. Now struggling with host == NULL. Digging into bounds inference stuff.

EDIT 2:

I added basic bounds inference checks, now it does not crash.. The next problem i have now, is: Is it possible to make call to external function, without actually influencing output result in direct manner?

Let me concretise what i mean.

The generator code looks like following:

    Buffer<bitType> lut{256, "lut"};

    args[0] = inputY;
    args[1] = lut;

    g.define_extern("generateAutoLevelsLut", args, { UInt(8) }, 2, Halide::NameMangling::C);

    outputY(x, y) = g(x, y); // Call line

    g.compute_root();
    outputY.compute_root();

Extern functon code fills second input 'lut' with some dummy LUT:

    Halide::Runtime::Buffer<uint16_t> im2Buffer(*input2);
    Mat im2Mat(Size(im2Buffer.width(), im2Buffer.height()), CVC_8U, im2Buffer.data(), im2Buffer.stride(1));

    for (int i = 0; i < 256; i++)
        im2Mat.at<uchar>(i) = i;

And if i comment the 'Call line' in generator, it optimizes away the call to extern at all. I want to make something like:

    Func lutRoot;
    lutRoot(x) = lut(x); // to convert from Buffer
    outputY(x, y) = autoLevelsPrecalcLut(inputY, lutRoot)(x, y);

And here lut is implicitly passed into extern and filled there. But it doesn't work, as well as other variants which ignore the modification of 'output'... or maybe this whole approach is wrong? Any suggestions? Thanks

EDIT 3:

Solved task avoiding extern calls, replacing while cycles with argmin and RDom combo, but original question about extern still remains

silver_rocket
  • 418
  • 1
  • 4
  • 15

1 Answers1

2

That should work (or fail with a linker error if it wasn't going to). It's possible the Halide pipeline doesn't think it needs to call your extern function. E.g. does something use the result?

Alternatively, try stderr instead, just in case it's an output stream buffering issue. That extern function definition is likely to cause Halide to error out (because it doesn't reply to the bounds inference query), and erroring out calls abort by default, which would swallow things printed to stdout.

Andrew Adams
  • 1,396
  • 7
  • 3
  • "It's possible the Halide pipeline doesn't think it needs to call your extern function" >> this. I added code that actually uses function's result and now it runs ! In my mind, compute_root was kinda forcing things to schedule, but this was obviously not correct. Wondering if Debug mode has the same behavior? I have another problem now, host pointer of output is null.. figuring this out now. – silver_rocket Aug 29 '19 at 13:26