1

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

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. 铸造 铸造是将cast<uint16_t>(clamped(x, y, c))每个 (x,y) 位置的 8 位 RG 和 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?基于此处brighten.cppCVPR'15 的示例,我可以使用在 X 方向上逐行矢量化,在 Y 方向上跨线程并行化.. 像这样?blur_x.vectorize(x, 4).parallel(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;
}
4

1 回答 1

2

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

因此声明:

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

定义为具有与16 位类型而不是 8 位类型input_16相同数量的通道。clampedHalide 中的算术与其最大操作数保持相同的位宽,并且与 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 或需要非平面存储布局时,还有其他方法可能更有意义。

于 2016-02-14T21:14:58.253 回答