数字形状是二进制图像(blob)中的一组连接像素。
它可以通过行程编码紧凑地表示,即将像素分组在水平线段中并存储起始端点坐标和长度。通常,RLC表示以光栅顺序存储运行,即逐行和从右到右。
对于平滑的形状,存储要求从O(N²)下降到O(N)。
形状的轮廓是一个封闭的像素链,当其内部被填充时(通过flood filling算法)恢复形状。它也是O(N)表示。 Wen的形状可用作位图,轮廓可以通过contouring算法获得。
我正在寻找一种算法,该算法直接计算给定其RLC表示形状的轮廓,而不是在中间位图中绘制它。预计该算法将在运行次数中按时间线性运行。
您是否遇到过解决方案?
答案 0 :(得分:1)
如果像素被填充但是与未填充的像素相邻,则像素是边界像素。给定填充像素的每行RLE编码,我们可以对三个相邻行进行操作以计算边界像素的RLE版本,然后对其进行解码。
基本上,我们有扫描线算法。有三行像
*********** ****
************************
**** ******
我们从RLE获得事件点(^
):
*********** ****
************************
**** ******
^ ^^ ^ ^ ^ ^^ ^
要做的第一件事是指定中间填充的像素,其上方或下方的空像素为边界。 (如果需要指导,间隔列表中设置差异的算法非常相似。)
*********** ****
BBB***BBBBBBBBBBB***BBBB
**** ******
然后,对于已填充但未知为边界的间隔,检查左端点是否在左侧有空间,右端点是否在右侧有空格。如果是这样(分别),它们就是边界。
答案 1 :(得分:0)
注意:这个答案假定"非大纲"意味着"被4个邻居"包围,所以结果与你的例子略有不同(1像素绿色而不是蓝色)。
所有轮廓像素都是不是所有4"相邻像素的像素。 (像素的左侧,右侧,上方,下方)已设置。
当从上到下解码RLC时,您可以使用以下伪代码算法获取轮廓像素:
For the first line
All decoded pixels are outline pixels
For the subsequent lines
Leftmost and rightmost pixels of each RLC run are outline pixels
All other pixels are outline pixels if:
The pixel above isn't set (case A)
The pixel below isn't set (case B)
案例A和B意味着您必须查看当前像素上方/下方的像素,因此该算法实际上应该是流水线/向前看一行,因为案例B将无法检测到下一行被解码。
编辑:要按顺时针顺序对像素进行排序,可以使用轮廓为对角线连接的单像素宽度线的事实。选取最顶行中的一个像素,您将有两个可能的下一个像素,跟随当前像素右下方,下方或右下方的像素。之后,只需按照您尚未访问过的相邻像素,直到没有相邻像素为止。例如:
/----- First pixel you pick, A and B are neighbour candidates, A is the "correct" one
v
xAxxx
B x
x x xxx
x xxxxxx x
xx x
xxxxxxxxxxx
s0123 Result after following the neighbours (s = start, e = end),
e 4 numbers from 0-9 show order of traversal
1 5 234
0 678901 5
98 6
76543210987
答案 2 :(得分:0)
<强>提示强>:
如其他答案中所述,发出轮廓像素列表可以实现为扫描线过程,在此期间检查运行端点的3x3邻域。
此过程将以乱码的方式发射像素,作为需要存储和重新排序的一系列直接和反向弧。
替代方案可以基于实现标准Moore Neighborhood算法的想法,该算法具有以所需顺序枚举轮廓像素的优势。
此过程需要知道当前像素周围的8邻域配置,并且想法是在每次移动到另一个像素时更新此邻域:我们维护包含当前像素和两个面向运行的运行的索引在上面和下面的行中。
在每次移动到另一个像素时,我们需要更新这三个索引,这将涉及排序运行列表中的短序列搜索。这可以看作是像素的伪随机访问机制,考虑到连续访问是强本地的,可以进行排序缓存。
<强>更新强>:
在我使用的游程编码表示中,只有黑色游程被编码为三元组(X, Y, L)
。这些运行按行从上到下排序,然后从左到右排成一行。
为方便起见,我们可以切换到“线性地址”方案,就像所有图像行已经相互追加一样,每个像素都由一个数字Z = X + Y.Nx
指定(其中Nx
是图像宽度。)
所以我们有一个黑色运行列表,并且在两个连续的黑色运行之间隐含地发现白色运行。
在处理过程中,我们可以随时记住在当前像素(R[I].Z <= Z < R[I+1].Z
)之前或之前开始的运行索引。我们可以通过检查我们是在运行中还是在它与下一个(Z < R[I].Z + R[I].L
)之间来判断像素的颜色。
如果我们向左移动一个位置,Z
会减少1
,我们可能必须选择上一个位置(I--
)。
如果我们向上移动一个位置,Z
会减少Nx
,我们可能需要多次回溯(I--
再次追溯R[I].Z <= Z
)。
图片显示了当前像素及其4个邻居,以及黑色运行的“影响区域”。 我们可以类似地处理所有八个位移方向。
正如我们所看到的,每次移动都需要进行一些操作,这些操作更接近于连续运行的次数,被认为是一个小值。使用这个概念,我们可以以合理的代价在任意路径上遍历RLC表示,而无需重建整个位图。
由于Moore邻域算法在轮廓长度上需要时间线性,基于此线性运行寻址的实现也将采用线性时间(对于每行的有限运行次数)。