Halide:如何处理(重叠)块中的图像?
Halide: How to process image in (overlapping) blocks?
我正在发现 Halide,并通过管道进行各种操作取得了一些成功
转换。其中大部分基于源中的示例(颜色转换、各种过滤器、hist-eq)。
我的下一步需要分块处理图像。在更一般的形式中,
部分重叠 个块。
例子
输入:
[ 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32]
非重叠块:
尺寸:2x4
[ 1, 2, 3, 4,
9, 10, 11, 12]
[ 5, 6, 7, 8,
13, 14, 15, 16]
[ 17, 18, 19, 20,
25, 26, 27, 28]
[ 21, 22, 23, 24,
29, 30, 31, 32]
重叠块:
尺寸:2x4,有 50% 重叠(两个轴)
[ 1, 2, 3, 4,
9, 10, 11, 12]
[ 3, 4, 5, 6,
11, 12, 13, 14]
[ 5, 6, 7, 8,
13, 14, 15, 16]
-
[ 9, 10, 11, 12,
17, 18, 19, 20]
[11, 12, 13, 14,
19, 20, 21, 22]
...
我怀疑应该有一种很好的方式来表达这些,因为它们也很常见
在许多算法中(例如宏块)。
我查看了什么
我试图从教程和示例应用程序中收集想法并发现以下内容,
这似乎与我想要实现的东西有些联系:
- Halide tutorial lesson 6: Realizing Funcs over arbitrary domains
// We start by creating an image that represents that rectangle
Image<int> shifted(5, 7); // In the constructor we tell it the size
shifted.set_min(100, 50); // Then we tell it the top-left corner
- 我遇到的问题:如何在不循环的情况下将其推广到多个移位域?
- Halide tutorial lesson 9: Multi-pass Funcs, update definitions, and reductions
- 这里介绍
RDom
,看起来不错,可以创建块视图
- 大多数使用
RDom
的示例似乎都是滑动的-window 就像没有跳跃的方法
目标
所以一般来说,我问的是如何实现一个基于块的视图,然后可以由
其他步骤。
如果该方法足够通用以实现重叠和不重叠,那就太好了
- 以某种方式先生成左上角的索引?
在我的例子中,图像维度在编译时已知,这简化了这个
- 但我仍然想要一些紧凑的形式,从 Halide 的角度来看它很适合使用(没有像那些带有小过滤器框的示例那样的手动编码的东西)
- 使用的方法可能取决于每个块的输出,在我的例子中是 标量
也许有人可以给我一些想法and/or一些例子(这会很有帮助)。
很抱歉没有提供代码,因为我认为我无法提供任何有用的东西。
编辑:解决方案
在 dsharlet 的回答和一些微小的 debugging/discussion here, the following very simplified self-containing code works (assuming an 1-channel 64x128 input like this one i created) 之后。
#include "Halide.h"
#include "Halide/tools/halide_image_io.h"
#include <iostream>
int main(int argc, char **argv) {
Halide::Buffer<uint8_t> input = Halide::Tools::load_image("TestImages/block_example.png");
// This is a simple example assuming an input of 64x128
std::cout << "dim 0: " << input.width() << std::endl;
std::cout << "dim 1: " << input.height() << std::endl;
// The "outer" (block) and "inner" (pixel) indices that describe a pixel in a tile.
Halide::Var xo, yo, xi, yi, x, y;
// The distance between the start of each tile in the input.
int tile_stride_x = 32;
int tile_stride_y = 64;
int tile_size_x = 32;
int tile_size_y = 64;
Halide::Func tiled_f;
tiled_f(xi, yi, xo, yo) = input(xo * tile_stride_x + xi, yo * tile_stride_y + yi);
Halide::RDom tile_dom(0, tile_size_x, 0, tile_size_y);
Halide::Func tile_means;
tile_means(xo, yo) = sum(Halide::cast<uint32_t>(tiled_f(tile_dom.x, tile_dom.y, xo, yo))) / (tile_size_x * tile_size_y);
Halide::Func output;
output(xo, yo) = Halide::cast<uint8_t>(tile_means(xo, yo));
Halide::Buffer<uint8_t> output_(2, 2);
output.realize(output_);
Halide::Tools::save_image(output_, "block_based_stuff.png");
}
这是一个将 Func 分成任意步长和大小的块的示例:
Func f = ... // The thing being blocked
// The "outer" (block) and "inner" (pixel) indices that describe a pixel in a tile.
Var xo, yo, xi, yi;
// The distance between the start of each tile in the input.
int tile_stride_x, tile_stride_y;
Func tiled_f;
tiled_f(xi, yi, xo, yo) = f(xo * tile_stride_x + xi, yo * tile_stride_y + yi);
Func tiled_output;
tiled_output(xi, yi, xo, yo) = ... // Your tiled processing here
要计算每个块的一些减少量(如统计数据),您可以执行以下操作:
RDom tile_dom(0, tile_size_x, 0, tile_size_y);
Func tile_means;
tile_means(xo, yo) = sum(tiled_output(tile_dom.x, tile_dom.y, xo, yo)) / (tile_size_x * tile_size_y);
将图块展平成结果有点棘手。这可能取决于您在重叠区域组合结果的方法。如果你想把重叠的瓷砖加起来,最简单的方法可能是使用 RDom:
RDom tiles_dom(
0, tile_size_x,
0, tile_size_y,
min_tile_xo, extent_tile_xo,
min_tile_yo, extent_tile_yo);
Func output;
Expr output_x = tiles_dom[2] * tile_stride_x + tiles_dom[0];
Expr output_y = tiles_dom[3] * tile_stride_y + tiles_dom[1];
output(x, y) = 0;
output(output_x, output_y) += tiled_output(tiles_dom[0], tiles_dom[1], tiles_dom[2], tiles_dom[3]);
请注意,在上面的两个代码块中,tile_stride_x 和 tile_size_x 是独立的参数,允许任何图块大小和重叠。
在您的两个示例中,tile_size_x = 4
和 tile_size_y = 2
。要获得不重叠的图块,请将图块步幅设置为等于图块大小。要获得 50% 的重叠图块,请设置 tile_stride_x = 2
和 tile_stride_y = 1
.
像这样的算法的有用时间表是:
// Compute tiles as needed by the output.
tiled_output.compute_at(output, tile_dom[2]);
// or
tiled_output.compute_at(tile_means, xo);
还有其他选项,例如使用纯 func(无 update/RDom),它使用 mod 运算符计算出图块的内部和外部索引。但是,这种方法可能难以有效地安排重叠的图块(取决于您在每个图块上进行的处理)。当这个问题出现时,我使用 RDom 方法。
请注意,使用 RDom 方法时,您必须提供要计算的图块索引的边界(min_tile_xo
、extent_tile_xo
、...),这对于重叠图块可能很棘手.
我正在发现 Halide,并通过管道进行各种操作取得了一些成功 转换。其中大部分基于源中的示例(颜色转换、各种过滤器、hist-eq)。
我的下一步需要分块处理图像。在更一般的形式中, 部分重叠 个块。
例子
输入:
[ 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32]
非重叠块:
尺寸:2x4
[ 1, 2, 3, 4,
9, 10, 11, 12]
[ 5, 6, 7, 8,
13, 14, 15, 16]
[ 17, 18, 19, 20,
25, 26, 27, 28]
[ 21, 22, 23, 24,
29, 30, 31, 32]
重叠块:
尺寸:2x4,有 50% 重叠(两个轴)
[ 1, 2, 3, 4,
9, 10, 11, 12]
[ 3, 4, 5, 6,
11, 12, 13, 14]
[ 5, 6, 7, 8,
13, 14, 15, 16]
-
[ 9, 10, 11, 12,
17, 18, 19, 20]
[11, 12, 13, 14,
19, 20, 21, 22]
...
我怀疑应该有一种很好的方式来表达这些,因为它们也很常见 在许多算法中(例如宏块)。
我查看了什么
我试图从教程和示例应用程序中收集想法并发现以下内容, 这似乎与我想要实现的东西有些联系:
- Halide tutorial lesson 6: Realizing Funcs over arbitrary domains
// We start by creating an image that represents that rectangle
Image<int> shifted(5, 7); // In the constructor we tell it the size
shifted.set_min(100, 50); // Then we tell it the top-left corner
- 我遇到的问题:如何在不循环的情况下将其推广到多个移位域?
- Halide tutorial lesson 9: Multi-pass Funcs, update definitions, and reductions
- 这里介绍
RDom
,看起来不错,可以创建块视图 - 大多数使用
RDom
的示例似乎都是滑动的-window 就像没有跳跃的方法
- 这里介绍
目标
所以一般来说,我问的是如何实现一个基于块的视图,然后可以由 其他步骤。
如果该方法足够通用以实现重叠和不重叠,那就太好了
- 以某种方式先生成左上角的索引?
在我的例子中,图像维度在编译时已知,这简化了这个
- 但我仍然想要一些紧凑的形式,从 Halide 的角度来看它很适合使用(没有像那些带有小过滤器框的示例那样的手动编码的东西)
- 使用的方法可能取决于每个块的输出,在我的例子中是 标量
也许有人可以给我一些想法and/or一些例子(这会很有帮助)。
很抱歉没有提供代码,因为我认为我无法提供任何有用的东西。
编辑:解决方案
在 dsharlet 的回答和一些微小的 debugging/discussion here, the following very simplified self-containing code works (assuming an 1-channel 64x128 input like this one i created) 之后。
#include "Halide.h"
#include "Halide/tools/halide_image_io.h"
#include <iostream>
int main(int argc, char **argv) {
Halide::Buffer<uint8_t> input = Halide::Tools::load_image("TestImages/block_example.png");
// This is a simple example assuming an input of 64x128
std::cout << "dim 0: " << input.width() << std::endl;
std::cout << "dim 1: " << input.height() << std::endl;
// The "outer" (block) and "inner" (pixel) indices that describe a pixel in a tile.
Halide::Var xo, yo, xi, yi, x, y;
// The distance between the start of each tile in the input.
int tile_stride_x = 32;
int tile_stride_y = 64;
int tile_size_x = 32;
int tile_size_y = 64;
Halide::Func tiled_f;
tiled_f(xi, yi, xo, yo) = input(xo * tile_stride_x + xi, yo * tile_stride_y + yi);
Halide::RDom tile_dom(0, tile_size_x, 0, tile_size_y);
Halide::Func tile_means;
tile_means(xo, yo) = sum(Halide::cast<uint32_t>(tiled_f(tile_dom.x, tile_dom.y, xo, yo))) / (tile_size_x * tile_size_y);
Halide::Func output;
output(xo, yo) = Halide::cast<uint8_t>(tile_means(xo, yo));
Halide::Buffer<uint8_t> output_(2, 2);
output.realize(output_);
Halide::Tools::save_image(output_, "block_based_stuff.png");
}
这是一个将 Func 分成任意步长和大小的块的示例:
Func f = ... // The thing being blocked
// The "outer" (block) and "inner" (pixel) indices that describe a pixel in a tile.
Var xo, yo, xi, yi;
// The distance between the start of each tile in the input.
int tile_stride_x, tile_stride_y;
Func tiled_f;
tiled_f(xi, yi, xo, yo) = f(xo * tile_stride_x + xi, yo * tile_stride_y + yi);
Func tiled_output;
tiled_output(xi, yi, xo, yo) = ... // Your tiled processing here
要计算每个块的一些减少量(如统计数据),您可以执行以下操作:
RDom tile_dom(0, tile_size_x, 0, tile_size_y);
Func tile_means;
tile_means(xo, yo) = sum(tiled_output(tile_dom.x, tile_dom.y, xo, yo)) / (tile_size_x * tile_size_y);
将图块展平成结果有点棘手。这可能取决于您在重叠区域组合结果的方法。如果你想把重叠的瓷砖加起来,最简单的方法可能是使用 RDom:
RDom tiles_dom(
0, tile_size_x,
0, tile_size_y,
min_tile_xo, extent_tile_xo,
min_tile_yo, extent_tile_yo);
Func output;
Expr output_x = tiles_dom[2] * tile_stride_x + tiles_dom[0];
Expr output_y = tiles_dom[3] * tile_stride_y + tiles_dom[1];
output(x, y) = 0;
output(output_x, output_y) += tiled_output(tiles_dom[0], tiles_dom[1], tiles_dom[2], tiles_dom[3]);
请注意,在上面的两个代码块中,tile_stride_x 和 tile_size_x 是独立的参数,允许任何图块大小和重叠。
在您的两个示例中,tile_size_x = 4
和 tile_size_y = 2
。要获得不重叠的图块,请将图块步幅设置为等于图块大小。要获得 50% 的重叠图块,请设置 tile_stride_x = 2
和 tile_stride_y = 1
.
像这样的算法的有用时间表是:
// Compute tiles as needed by the output.
tiled_output.compute_at(output, tile_dom[2]);
// or
tiled_output.compute_at(tile_means, xo);
还有其他选项,例如使用纯 func(无 update/RDom),它使用 mod 运算符计算出图块的内部和外部索引。但是,这种方法可能难以有效地安排重叠的图块(取决于您在每个图块上进行的处理)。当这个问题出现时,我使用 RDom 方法。
请注意,使用 RDom 方法时,您必须提供要计算的图块索引的边界(min_tile_xo
、extent_tile_xo
、...),这对于重叠图块可能很棘手.