My goal is to be able to model signal-dependent Gaussian noise in Halide. I have a model built in OpenCV which I am now porting to Halide. The challenge is that Halide's random number generator is not normally distributed, so I need to use an external function to produce the noise values.
The implementation attempt uses the C++ random number generator to produced normally distributed noise, a Halide Func to produce the signal-depended standard deviations of the noise at each pixel, and then the noise is added to the pixels in renoise. Below I show the layout of the functions.
// Note: This is an implementation of the noise model found in the paper below:
// "Noise measurement for raw-data of digital imaging sensors by
// automatic segmentation of non-uniform targets"
float get_normal_dist_rand( float mean, float std_dev ) {
std::default_random_engine generator;
std::normal_distribution<float> distribution(mean,std_dev);
float out = distribution(generator);
return out;
}
Func make_get_std_dev( Func *in_func ) {
Var x, y, c;
float q = 0.0060;
float p = 0.0500;
// std_dev = q * sqrt(unnoised_pixel - p)
Func get_std_dev("get_std_dev");
get_std_dev(x,y,c) = q * sqrt( (*in_func)(x,y,c) - p );
return get_std_dev;
}
Func make_renoise( Func *in_func, Func *std_dev ) {
Var x, y, c;
// Noise parameters
// noised_pixel = unnoised_pixel +
// gaussian_rand( std_dev = q * sqrt(unnoised_pixel - p) )
// q and p values do not vary between channels
Func renoise("renoise");
renoise(x,y,c) = (*in_func)(x,y,c) +
get_normal_dist_rand(0,(*std_dev)(x,y,c));
return renoise;
}
This makes sense to me, but unfortunately I receive the following error when I try to compile:
../common/pipe_stages.cpp: In function 'Halide::Func make_renoise(Halide::Func*, Halide::Func*)':
../common/pipe_stages.cpp:223:64: error: cannot convert 'std::enable_if<true, Halide::FuncRef>::type {aka Halide::FuncRef}' to 'float' for argument '2' to 'float get_normal_dist_rand(float, float)'
get_normal_dist_rand(0,(*std_dev)(x,y,c));
^
So it seems that the output of a Func cannot be provided to a C++ function. I guess this makes sense as a limitation of Halide, but I don't really see an alternative to implement the signal dependent normally distributed noise. Is there another way to use external C++ functions in Halide? I have seen folks talking about using "extern" but unfortunately documentation on that functionality seems to be quite light, and I am unable to find what I need.
You'll need to use one of our extern mechanisms to bind to C++ code. HalideExtern_* is the easier of the two and will let you make a call to get random numbers one at a time. Alas test/correctness/c_function.cpp is the immediate example for this, which will help, but could be clearer.
I expect you'll want to request a buffer of random numbers at a time for efficiency reasons. This can be done via the define_extern mechanism. The C++ function has to participate in bounds inference so it is a little more involved. The test for this is correctness/extern_producer.cpp.
I'd expect either transforming our random numbers to be appropriately distributed or implementing the random number generation algorithm in Halide is the right way to go for really fast production code, but that is likely more work than you want to do to get this working initially.