【问题标题】:Adapting scanline fill to detect discrete objects调整扫描线填充以检测离散对象
【发布时间】:2013-03-31 21:45:22
【问题描述】:

我正在尝试在 Python 中检测颜色足够接近的连续区域。我独立地偶然发现了 8 路递归洪水填充算法(当找到的和所需的 RGB 颜色之间的欧几里德距离超过阈值时终止),该算法在小规模上效果很好,但会导致 2 兆像素图像上的堆栈溢出。

Stack Overflow 和 Wikipedia 将扫描线填充作为答案,但我发现的每个解释要么是 C++ 中的,要么是关于用已知顶点填充多边形的。有人可以为类似于递归洪水填充的情况指出一个很好的伪代码解释吗?

由于缺乏正式的数学(我在高中),我在研究图像分割方面遇到了困难。如果有对 K-Means 或类似内容的简单英语解释,那就是也很棒。 OpenCV 看起来很有希望,但似乎我得到的只是一个颜色扁平化的图像;我只关心对象中 x,y 处的像素列表。

【问题讨论】:

    标签: python image-processing image-segmentation


    【解决方案1】:

    scanline flood fill的思路如下

    1. 给你初始点(种子)(x, y)
    2. 尽可能向左走,直到像素 (x-1, y) 未被填充或到达 x=0
    3. 您到达的 x 将是扫描线的起点;保留两个标志“寻找上方的洞穴”和“寻找下方的洞穴”,均初始化为 true
    4. 这是扫描线循环的开始。检查 (x, y-1) 上面的像素是否要填充,现在考虑上视标志和像素有 4 种情况:
      • 如果“往上看”为真并且要填充像素,那么您找到了一个“新洞穴”,将 (x, y-1) 存储在“待办事项列表”中并将“往上看”标记为错误
      • 如果“look above”为假且不填充像素,则当前洞穴已完成,您需要寻找另一个洞穴,因此只需将“look_above”标记为 true
      • 在其他情况下(看上面是真的,不填充像素或看上面是假的,要填充像素)你什么都不做
    5. 对“往下看”标志和像素颜色 (x, y+1) 重复相同的推理
    6. 绘制像素 (x, y) 并移动到 (x+1, y),从 (5) 开始重复,除非移动到的像素不会被绘制。
    7. 如果“待办事项列表”中有任何内容,请使用您在待办事项列表中找到的坐标 (x, y) 将其选中并返回 (1) 处

    这是 4 连接洪水填充的版本。对于 8 连接填充,您还需要在开始扫描线时检查 (x-1, y-1) 和 (x-1, y+1) 处的洞穴,并且需要检查洞穴 (x+1, y -1) 和 (x+1, y+1) 在扫描线结束(如果相应的标志为真)。

    在扫描线上移动时,您要做的是将图片中的绿点添加到您的待办事项列表中:

                                                         

    请注意,种子的数量不会是“最少的”(例如,示例中的前两个“种子之上”最终会在同一个洞穴中,因此只需要其中一个)。尽管如此,存储它们所需的堆栈数量通常比逐像素递归方法所需的数量要小得多。

    另一种限制所需内存量的可能方法是使用边界绘制算法:

    1. 您将初始种子 (x, y) 放入 current_active 列表并绘制它
    2. next_active 列表初始化为空
    3. current_active 列表中的每个像素检查需要绘制的邻居:当您找到一个时,将其绘制并添加到next_active 列表中
    4. 完成后,将 current_active 列表设置为 next_active 列表并从 2 开始重复,除非列表为空

    您可以在this video 中查看这两种算法的示例。

    【讨论】:

    • 哇!谢谢提供信息。扫描线现在终于有意义了。我实际上对“前沿”算法更感兴趣,这似乎是我正在做的迭代等价物。为什么 Python 可以轻松处理超过 30,000 个项目的列表,但是当我尝试超过 10,000 的递归深度时却失败了?
    • 我实际上采用了您的“前沿”方法,因为它似乎最接近最初的想法并且速度非常快。仍然很有趣,为什么这比递归方法快得多……
    • @jacobbaer:CPython 不能处理无限递归,因为它依赖于解释器中的 C 堆栈,而且 C 堆栈的限制有时非常低。您可以使用sys.setrecursionlimit 提高python 限制,但是将其设置得太高只会使python 本身崩溃而不是引发异常。另请注意,Python 不处理尾调用优化,纯函数式编程视图并未真正得到认可。递归当然没问题,但深度递归(例如使用递归遍历链表)是个坏主意。
    • @6502 如何检查您是否已经归档了一行,这样您就不会多次向上或向下检查像素,或者检查多个像素是不可避免的。
    • 在填充扫描线时,您看起来“上方”(即 y-1),每次看到新的光圈时,您都会放置一个种子,但只有一个,直到您看到天花板再次关闭。假设你在上面看到1100001111100011111011111,你只需要在第三、十二和第二十个位置放置一个种子。当然,可能不需要放置种子,因为多个孔可能是同一个洞穴的一部分,但如果没有全局搜索,您无法知道。
    猜你喜欢
    • 2014-05-08
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-10
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    相关资源
    最近更新 更多