在没有定义外部函数的Halide中实现Canny算法的滞后步骤的困难


  • 问题是,当标记为弱边(在两个阈值之间)的像素变为强边(接受,如本文所述)时,需要递归地将相同的逻辑应用于连接的邻居(跟踪边)
  • 在命令式语言中,当从弱边变为强边时,可以使用Stack来存储位置(x,y)。然后,在最后,在堆栈不为空的情况下处理邻居,根据需要更新堆栈。但是,在没有define_extern函数的情况下,如何在纯Halide中实现类似的功能

我已经使用了这段代码来进行滞后,但缺乏动态递归和/或堆栈来在需要时对邻居进行滞后,这是我找不到如何实现的:

magnitude = input(x, y);
// mask receives only 0, 1, or 2.
mask(x, y) = select(magnitude > high_threshold, 2/*strong-edge*/, magnitude < low_threshold, 0/*no-edge*/, 1/*weak-edge*/);
// when mask(x,y) == 1 checks the neighbors to decide.
hysteresis(x, y) = select(mask(x, y) == 0, 0,   mask(x, y) == 2, 255,
mask(x-1, y-1) == 2 || mask(x, y-1) == 2 || mask(x+1, y-1) == 2 ||
mask(x-1, y) == 2 || mask(x+1, y) == 2 ||
mask(x-1, y+1) == 2 || mask(x, y+1) == 2 || mask(x+1, y+1) == 2, 255/*weak-to-strong edge*/, 0);

毫无疑问,有没有一种方法可以通过递归、堆栈或其他任何东西来做这样的事情:

if (hysteresis(x, y) above changes from weak to strong edge, do) {
hysteresis(x-1, y-1); hysteresis(x, y-1); hysteresis(x+1, y-1);
hysteresis(x-1, y); hysteresis(x+1, y);
hysteresis(x-1, y+1); hysteresis(x, y+1); hysteresis(x+1, y+1);
}

简短回答:不。

没有办法使用非图像数据结构(如堆栈),也没有办法进行动态递归。目前尚不清楚Halide是否真的会在这里增加太多价值,因为该算法在编写时似乎不可分块、可并行或可向量化。

然而,您可以将该算法重写为对图像从弱到强翻转边缘进行迭代扫描的算法。它可以被认为是一个在三种状态(弱、强、非边缘)上运行到完成的元胞自动机,我们可以对每个过程进行矢量化/并行化。有关示例,请参阅Halide repo中的test/corrective/gameoflife.cpp。不过,我认为这种方法的计算复杂度会很糟糕。你会在每个像素上工作,而不仅仅是在翻转的像素的活动边缘。

你也可以把它作为一个细胞自动机来运行,它沿着一些波前进行原位更新,例如从上到下、从下到上、从左到右、从右到左进行扫描。然后可以沿着波前进行矢量化。时间表类似于IIR(参见https://github.com/halide/CVPR2015/blob/master/RecursiveFilter/IirBlur.cpp)。这种方法可以处理沿任何方向的线性边,但任何固定次数的扫描都会错过从弱到强的螺旋。

但是,与其用这些方式扭曲代码,我只需要使用不同的算法,或者使用define_extern。