This source file includes following definitions.
- build
- downsample
- upsample
#include "Halide.h"
namespace {
constexpr int maxJ = 20;
class LocalLaplacian : public Halide::Generator<LocalLaplacian> {
public:
GeneratorParam<int> pyramid_levels{"pyramid_levels", 8, 1, maxJ};
ImageParam input{UInt(16), 3, "input"};
Param<int> levels{"levels"};
Param<float> alpha{"alpha"};
Param<float> beta{"beta"};
Func build() {
const int J = pyramid_levels;
Func remap;
Expr fx = cast<float>(x) / 256.0f;
remap(x) = alpha*fx*exp(-fx*fx/2.0f);
Func clamped = Halide::BoundaryConditions::repeat_edge(input);
Func floating;
floating(x, y, c) = clamped(x, y, c) / 65535.0f;
Func gray;
gray(x, y) = 0.299f * floating(x, y, 0) + 0.587f * floating(x, y, 1) + 0.114f * floating(x, y, 2);
Func gPyramid[maxJ];
Expr level = k * (1.0f / (levels - 1));
Expr idx = gray(x, y)*cast<float>(levels-1)*256.0f;
idx = clamp(cast<int>(idx), 0, (levels-1)*256);
gPyramid[0](x, y, k) = beta*(gray(x, y) - level) + level + remap(idx - 256*k);
for (int j = 1; j < J; j++) {
gPyramid[j](x, y, k) = downsample(gPyramid[j-1])(x, y, k);
}
Func lPyramid[maxJ];
lPyramid[J-1](x, y, k) = gPyramid[J-1](x, y, k);
for (int j = J-2; j >= 0; j--) {
lPyramid[j](x, y, k) = gPyramid[j](x, y, k) - upsample(gPyramid[j+1])(x, y, k);
}
Func inGPyramid[maxJ];
inGPyramid[0](x, y) = gray(x, y);
for (int j = 1; j < J; j++) {
inGPyramid[j](x, y) = downsample(inGPyramid[j-1])(x, y);
}
Func outLPyramid[maxJ];
for (int j = 0; j < J; j++) {
Expr level = inGPyramid[j](x, y) * cast<float>(levels-1);
Expr li = clamp(cast<int>(level), 0, levels-2);
Expr lf = level - cast<float>(li);
outLPyramid[j](x, y) = (1.0f - lf) * lPyramid[j](x, y, li) + lf * lPyramid[j](x, y, li+1);
}
Func outGPyramid[maxJ];
outGPyramid[J-1](x, y) = outLPyramid[J-1](x, y);
for (int j = J-2; j >= 0; j--) {
outGPyramid[j](x, y) = upsample(outGPyramid[j+1])(x, y) + outLPyramid[j](x, y);
}
Func color;
float eps = 0.01f;
color(x, y, c) = outGPyramid[0](x, y) * (floating(x, y, c)+eps) / (gray(x, y)+eps);
Func output("local_laplacian");
output(x, y, c) = cast<uint16_t>(clamp(color(x, y, c), 0.0f, 1.0f) * 65535.0f);
remap.compute_root();
if (get_target().has_gpu_feature()) {
Var xi, yi;
output.compute_root().gpu_tile(x, y, xi, yi, 16, 8);
for (int j = 0; j < J; j++) {
int blockw = 16, blockh = 8;
if (j > 3) {
blockw = 2;
blockh = 2;
}
if (j > 0) {
inGPyramid[j].compute_root().gpu_tile(x, y, xi, yi, blockw, blockh);
gPyramid[j].compute_root().reorder(k, x, y).gpu_tile(x, y, xi, yi, blockw, blockh);
}
outGPyramid[j].compute_root().gpu_tile(x, y, xi, yi, blockw, blockh);
}
} else {
Var yo;
output.reorder(c, x, y).split(y, yo, y, 64).parallel(yo).vectorize(x, 8);
gray.compute_root().parallel(y, 32).vectorize(x, 8);
for (int j = 1; j < 5; j++) {
inGPyramid[j]
.compute_root().parallel(y, 32).vectorize(x, 8);
gPyramid[j]
.compute_root().reorder_storage(x, k, y)
.reorder(k, y).parallel(y, 8).vectorize(x, 8);
outGPyramid[j]
.store_at(output, yo).compute_at(output, y)
.vectorize(x, 8);
}
outGPyramid[0]
.compute_at(output, y).vectorize(x, 8);
for (int j = 5; j < J; j++) {
inGPyramid[j].compute_root();
gPyramid[j].compute_root().parallel(k);
outGPyramid[j].compute_root();
}
}
return output;
}
private:
Var x, y, c, k;
Func downsample(Func f) {
using Halide::_;
Func downx, downy;
downx(x, y, _) = (f(2*x-1, y, _) + 3.0f * (f(2*x, y, _) + f(2*x+1, y, _)) + f(2*x+2, y, _)) / 8.0f;
downy(x, y, _) = (downx(x, 2*y-1, _) + 3.0f * (downx(x, 2*y, _) + downx(x, 2*y+1, _)) + downx(x, 2*y+2, _)) / 8.0f;
return downy;
}
Func upsample(Func f) {
using Halide::_;
Func upx, upy;
upx(x, y, _) = 0.25f * f((x/2) - 1 + 2*(x % 2), y, _) + 0.75f * f(x/2, y, _);
upy(x, y, _) = 0.25f * upx(x, (y/2) - 1 + 2*(y % 2), _) + 0.75f * upx(x, y/2, _);
return upy;
}
};
Halide::RegisterGenerator<LocalLaplacian> register_me{"local_laplacian"};
}