【问题标题】:Need help implementing flood-fill algorithm需要帮助实施洪水填充算法
【发布时间】:2012-07-31 18:37:17
【问题描述】:

所以我正在尝试创建一个洪水填充算法,但我不断收到递归错误。该算法似乎具有无限递归,我无法确定原因。我浏览了整个互联网,但我找不到解决方案,因为根据大多数消息来源,我的程序似乎是正确的。然而,似乎有什么不对劲。这是代码的编辑版本。错误信息仍然是最大递归。

我能得到一些帮助吗?

from PIL import Image, ImageTk
from random import *


w= 75
h= w

flood = Image.new("RGB", (w,h), (0,0,0))

x = 0
y = 0
count = 0

colorlist = []
i = 0

while x < w -1:
    y = 0
    while y < h-1:
        r = random()
        if r < .25:
            flood.putpixel((x,y), (0,0,0))
        else:
            flood.putpixel((x,y), (255,255,255))
        y += 1
    x += 1
x = 0
y = 0
while x < w-1:
    y = 0
    while y < h-1:
        r = random()
        if x == 0 or y == 0 or x == w-1 or y ==h-1:
            flood.putpixel((x,y), (0,0,0))
        y += 1
    x += 1


def floodfill(x,y, d,e,f, g,h,i, image, count):
        count+=1
        (a,b,c) = image.getpixel((x,y))
        if (a,b,c) == (255,255,255):
            (j,k,l) = image.getpixel((x-1,y))
            (m,n,o) = image.getpixel((x+1, y))
            (p,q,r) = image.getpixel((x,y-1))
            (s,t,u) = image.getpixel((x,y+1))
        if count > 990:
            return
        if (a,b,c) == (255,255,255):
            image.putpixel((x,y), (g,h,i))
            floodfill(x-1, y, d,e,f, g,h,i, image, count)
            floodfill(x+1, y, d,e,f, g,h,i, image, count)
            floodfill(x, y-1, d,e,f, g,h,i, image, count)
            floodfill(x, y+1, d,e,f, g,h,i, image,count)


floodfill(2,2, 0,0,0,255,0,0,flood, 0)

flood.save("flood.png")
print("done")

【问题讨论】:

  • 请提供您看到的确切错误消息。
  • RuntimeError: 调用 Python 对象时超出最大递归深度
  • 您似乎没有检查 x,y 是否越界 - 当 x 快速向左移动时,这可能导致无限递归。还是您希望您的边界能解决这个问题?
  • Python 的递归限制相对较低,大约为 1000 - 这是 Python 的陷阱,适用于习惯于具有尾递归优化(如方案)的函数式语言的人。
  • 好的,所以我添加了更多边框,现在它不会超出范围。它适用于较小尺寸的图像,但是当您增加图像的大小时,它会增加递归量,从而使其达到最大值。

标签: python graphics python-imaging-library


【解决方案1】:

Python 倾向于抛出maximum recursion depth exceeded 错误,即使该算法不会无限递归并且最终会自行停止。有两种解决方案:增加递归限制,或切换到迭代算法。

您可以使用sys.setrecursionlimit 提高递归限制。选择一个高于算法最坏情况递归深度的数字。在您的情况下,这将是您图像中的像素数,length * height

将您的算法更改为迭代算法相当简单,因为绘制像素的顺序并不重要,只要您至少获得一次即可。 set 非常适合保存唯一的无序数据,所以让我们用它来存储我们需要绘制的像素。

def floodFill(x,y, d,e,f, g,h,i, image):
    toFill = set()
    toFill.add((x,y))
    while not toFill.empty():
        (x,y) = toFill.pop()
        (a,b,c) == image.getpixel((x,y))
        if not (a,b,c) == (255, 255, 255):
            continue
        image.putpixel((x,y), (g,h,i))
        toFill.add((x-1,y))
        toFill.add((x+1,y))
        toFill.add((x,y-1))
        toFill.add((x,y+1))
    image.save("flood.png")

如果您确实使用迭代方法,请务必在其中进行边界检查。否则,它可能会永远运行!或者至少在你的硬盘被一个巨大的toFill 集填满之前。

【讨论】:

    【解决方案2】:

    为什么不以depth-first 方式填充填充,而不是递归呢?递归无论如何都使用隐式堆栈,因此您不会有任何损失。

    是的,正如 cmets 中所指出的,您应该检查 x 和 y 是否超出范围。

    【讨论】:

    • 您是说 BFS 吗?因为从我对 OPs 代码的理解来看,该代码似乎是 DFS。如果我错了,请纠正我。
    【解决方案3】:

    这尚未经过测试,但主要基于您提供的代码。它应该可以工作,并提供了一种实现floodfill 算法的替代方法。该功能可能会更有效。

    import PIL
    import random
    import collections
    
    WHITE = 255, 255, 255
    BLACK = 0, 0, 0
    RED = 255, 0, 0
    
    def main(width, height):
        flood = PIL.Image.new('RGB', (width, height), BLACK)
        # Create randomly generated walls
        for x in range(width):
            for y in range(height):
                flood.putpixel((x, y), BLACK if random.random() < 0.15 else WHITE)
        # Create borders
        for x in range(width):
            for y in range(height):
                if x in {0, width - 1} or y in {0, height - 1}:
                    flood.putpixel((x, y), BLACK)
        floodfill(50, 25, RED, image)
        # Save image
        image.save('flood.png')
    
    def floodfill(x, y, color, image):
        # if starting color is different from desired color
        #     create a queue of pixels that need to be changed
        #     while there are pixels that need their color changed
        #         change the color of the pixel to what is desired
        #         for each pixel surrounding the curren pixel
        #             if the new pixel has the same color as the starting pixel
        #                 record that its color needs to be changed
        source = image.getpixel((x, y))
        if source != color:
            pixels = collections.deque[(x, y)]
            while pixels:
                x, y = place = pixels.popleft()
                image.putpixel(place, color)
                for x_offset in -1, 1:
                    x_offset += x
                    for y_offset in -1, 1:
                        y_offset += y
                        new_place = x_offset, y_offset
                        if image.getpixel(new_place) == source:
                            pixels.append(new_place)
    
    if __name__ == '__main__':
        main(100, 50)
    

    【讨论】:

    • 好的,我知道了,这只是使用 setrecursionlimit 函数的问题。谢谢大家!
    猜你喜欢
    • 1970-01-01
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-16
    相关资源
    最近更新 更多