Halide:投射RGB图像并平行模糊

时间:2016-02-11 23:42:37

标签: c++ halide

以下代码改编自Halide教程。

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)重载的算术运算? E.g。

  3. (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 here中的brighten.cpp示例,我可以使用blur_x.vectorize(x, 4).parallel(y);在X方向上逐行向量化,在Y方向上跨越线程并行化..像这样?
    2. 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;
      }
      

1 个答案:

答案 0 :(得分:2)

问题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位类型(在整个图像上采用的给定颜色通道的平均值不应改变)。上面的代码在两个地方执行upcast和downcast,这是多余的。它可能不会导致任何性能影响,因为编译器应该足够智能以识别外部转换集是nops,但它不会导致特别可读的代码。

问题2:实际上是相同的答案。我不会使用术语&#34;重载&#34;这里,但定义适用于所有坐标。 Var&#34; c&#34;在左侧和右侧提到并且每个都具有相同的值。 (我们有一个简写下划线(&#39; _&#39;)表示&#34;零个或多个坐标&#34;允许通过参数列表,但在这些定义中没有什么特别的。)< / p>

问题3:为矢量化和并行化安排此方法的最简单方法是使用平面布局(所有R值彼此相邻存储,然后是所有G等)并矢量化为适当大小16比特数学。 (例如&#34; vectorize(x,natural_vector_size())&#34; id在Generator中工作。)线程并行沿着行 - &#34; .parallel(y)&#34;。根据行的长度,您可能希望将拆分参数添加到并行指令。

此计划也适用于半平面表示(一行R,一行G和一行B)。

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