【问题标题】:Halide: casting RGB images and parallelising blur卤化物:投射 RGB 图像和并行化模糊
【发布时间】:2016-02-11 23:42:36
【问题描述】:

以下代码改编自卤化物教程。

Func blurX(Func continuation)
{ Var x("x"), y("y"), c("c");
  Func input_16("input_16");
  input_16(x, y, c) = cast<uint16_t>(continuation(x, y, c));
  Func blur_x("blur_x");
  blur_x(x, y, c) = (input_16(x-1, y, c) +
                     2 * input_16(x, y, c) +
                     input_16(x+1, y, c)) / 4;
  Func output("outputBlurX");
  output(x, y, c) = cast<uint8_t>(blur_x(x, y, c));
  return output;
}

int main()
{ Var x("x"), y("y"), c("c");
  Image<uint8_t> input = load_image("input.png");
  Func clamped("clamped");
  clamped = BoundaryConditions::repeat_edge(input);
  Func img1Fun("img1Fun");
  Func img2Fun = blurX(clamped);
  Func outputFun("outputFun");
  /* carry on */
}

我有三个问题:

  1. Casting 是演员cast&lt;uint16_t&gt;(clamped(x, y, c)) 将每个 (x,y) 位置的 8 位 R G 和 B 值转换为 16 位整数,即演员返回的是一个 RGB 图像,可以被索引例如 img1Fun(x, y, 0) 以访问其 R 值?或者这是将图像中的每个 RGB 像素投射到每个 (x,y) 位置的 RGB 像素的 [0..1] 之间的亮度值,即r*0.3 + g*0.59 + b*0.11

  2. 重载 RGB 模糊 是否在所有索引上重载了 (x,y,c) 的算术运算?例如

(input_16(x-1, y, c) + 2 * input_16(x, y, c) + input_16(x+1, y, c)) / 4; 

这是一个重载:

(input_16(x-1, y, 0) + 2 * input_16(x, y, 0) + input_16(x+1, y, 0)) / 4;
(input_16(x-1, y, 1) + 2 * input_16(x, y, 1) + input_16(x+1, y, 1)) / 4;
(input_16(x-1, y, 2) + 2 * input_16(x, y, 2) + input_16(x+1, y, 2)) / 4;
  1. 并行化如何并行化blurX?基于来自 CVPR'15 herebrighten.cpp 示例,我可以使用 blur_x.vectorize(x, 4).parallel(y); 在 X 方向上逐行矢量化,在 Y 方向上跨线程并行化。像这样?
Func blurX(Func continuation)
{ Var x("x"), y("y"), c("c");
  Func input_16("input_16");
  input_16(x, y, c) = cast<uint16_t>(continuation(x, y, c));
  Func blur_x("blur_x");
  blur_x(x, y, c) = (input_16(x-1, y, c) +
                     2 * input_16(x, y, c) +
                     input_16(x+1, y, c)) / 4;
  blur_x.vectorize(x, 4).parallel(y);
  Func output("outputBlurX");
  output(x, y, c) = cast<uint8_t>(blur_x(x, y, c));
  return output;
}

【问题讨论】:

    标签: c++ halide


    【解决方案1】:

    问题 1:Func 定义了从一组坐标到 Expr 的抽象映射,这是这些坐标的数学函数。一般来说,运算符是直截了当的,并且没有任何成像特定的行为,例如将颜色元组转换为光度标量。 (要完成这种转换,必须编写代码,因为系数取决于所使用的色彩空间。)

    因此声明:

    img1Fun(x, y, c) = cast<uint16_t>(clamped(x, y, c));
    

    input_16 定义为与clamped 具有相同数量的通道,但使用16 位类型而不是8 位类型。 Halide 中的算术与其最大操作数保持相同的位宽,并且与 C 不同,它不会隐式向上转换为标准 int 大小。这是因为通过矢量化,保持对通道大小的明确控制非常重要。在这种情况下,需要使用 16 位中间类型以避免在对 8 位值求和时溢出。

    除法后有一个相应的转换回 8 位类型。模糊结果保证适合 8 位类型,因为计算是标准化的(给定颜色通道在整个图像上的平均值不应该改变)。上面的代码在两个地方都进行了向上转换和向下转换,这是多余的。它可能不会对性能产生任何影响,因为编译器应该足够聪明,可以识别外部强制转换是 nop,但它不会产生特别可读的代码。

    问题 2:实际上是相同的答案。我不会在这里使用术语“重载”,但该定义适用于所有坐标。 Var“c”在左手边和右手边被提及,并且在每一个上都具有相同的值。 (我们有一个简写的下划线 ('_') 表示法来表示“零个或多个坐标”以允许通过参数列表,但除此之外,这些定义没有什么特别之处。)

    问题 3:为矢量化和并行化安排此操作的最简单方法是使用平面布局(所有 R 值彼此相邻存储,然后是所有 G 等)并将矢量化为 16 的适当大小位数学。 (例如,“vectorize(x, natural_vector_size())” id 在生成器中工作。)沿行的线程并行性——“.parallel(y)”。根据行的长度,您可能需要在并行指令中添加拆分参数。

    此时间表也适用于半平面表示(R 行、G 行和 B 行)。

    当 blurX 用于实际管道的上下文或需要非平面存储布局时,还有其他方法可能更有意义。

    【讨论】:

    • 谢谢!对于第一季度,我编辑了代码,以便向上转换和向下转换只发生一次。对于第三季度,我在 blurX 的并行化中遇到了运行时错误,为此我提出了一个 github 问题 github.com/halide/Halide/issues/1025。关于 natural_vector_size(),是否可以在扩展 Halide::Generator 的类中不使用它?即我刚刚在 C++ 文件github.com/robstewart57/… 中获得了一组函数@ 在不扩展 Halide::Generator 时我还能使用 natural_vector_size() 吗?
    • Target::natural_vector_size 是 Generator 使用的底层例程,可以在任何 Target 上调用。例如。 "Halide::get_target_from_environment()::natural_vector_size()" 会起作用。
    • 是的,Halide::get_target_from_environment().natural_vector_size() 对我有用,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多