以下代码改编自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 */
}
我有三个问题:
投射投射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
?
重载RGB模糊是对所有索引上的(x,y,c)
重载的算术运算? E.g。
(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;
blurX
?根据CVPR'15 here中的brighten.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;
}
答案 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或者需要非平面存储布局时,还有其他方法可能更有意义。