【问题标题】:How to get the area of shape filled using turtle如何使用乌龟填充形状区域
【发布时间】:2019-05-14 00:33:27
【问题描述】:

我使用海龟在 Python 中绘制了一个分形形状,并试图在足够高的迭代后得到这个分形的面积。对于那些感兴趣的人来说,这个分​​形与科赫雪花有关。

我能够使用 begin_fill() 和 end_fill() 用黑色填充分形。然后我使用this answer 来获取有效范围内每个像素的颜色。如果它不等于白色,那么我在计数中加了一个。此解决方案适用于分形的小迭代。但是,尝试进行更高的迭代时会花费大量时间。

这是我的分形代码。

def realSnowflake(length, n, s, show = False):
    #n: after n iterations
    #s: number of sides (in Koch snowflake, it is 3)
    #length: starting side length
    turtle.begin_fill()
    a = 360/s
    for i in range(s):
        snowflake(length, n, s) 
        turtle.right(a)
    turtle.end_fill()

这是我查找该区域的代码。

count = 0
canvas = turtle.getcanvas()
for x in range(x1, x2+1): #limits calculated through math
    for y in range(y2, y1+1):
        if get_pixel_color(x, y, canvas) != "white":
            count += 1

我希望能够更快地找到这个分形的面积。花费最多时间的不是绘制分形图,而是 x 和 y 的双重 for 循环。我认为如果有办法在海龟填充时找到该区域,这将是最佳选择。

【问题讨论】:

  • 在海龟绘制分形时查找区域可能不会比一次性计算最终结果更有效。但是,绘制的图像的复杂性不应影响计算黑色像素所需的时间,因为对于任何图像都必须访问和比较相同数量的像素 - 所以我认为你的问题在于你没有共享的代码这里。你在哪里调用这个代码?而realSnowflake函数中的snowflake函数又是什么?也许你应该分享一个最小的、可验证的和完整的例子stackoverflow.com/help/mcve
  • 就像我在评论中所说的那样,在绘图期间进行计算不太可能提高性能。就像您在问题中所说的那样,当您绘制更复杂的分形时,性能似乎会变得更糟,考虑到您提出的解决方案,这没有任何意义 - 因此很可能得出问题出在您的代码的其余部分的结论。但也许你的问题不是编码问题,而是沟通问题。

标签: python turtle-graphics fill area fractals


【解决方案1】:

绘制图像的复杂性不应该影响它所花费的时间 计算黑色像素

不幸的是,在这种情况下确实如此。如果我们查找earlier source of the get_pixel_color() code,我们会找到说明文字“很慢”。但更糟糕的是,它实际上变慢了!

此代码构建在canvas.find_overlapping() 之上,它正在寻找位于 X、Y 上的高级对象。在tkinter为turtle填充对象的情况下,有重叠,在下面的代码中最多三层。随着事实变得更加复杂,这种情况会增加。这是我的代码来证明这一点:

from turtle import Screen, Turtle
from math import floor, ceil
from time import time

def koch_curve(turtle, iterations, length):
    if iterations == 0:
        turtle.forward(length)
    else:
        for angle in [60, -120, 60, 0]:
            koch_curve(turtle, iterations - 1, length / 3)
            turtle.left(angle)

def koch_snowflake(turtle, iterations, length):
    turtle.begin_poly()
    turtle.begin_fill()

    for _ in range(3):
        koch_curve(turtle, iterations, length)
        turtle.right(120)

    turtle.end_fill()
    turtle.end_poly()

    return turtle.get_poly()

def bounding_box(points):
    x_coordinates, y_coordinates = zip(*points)
    return [(min(x_coordinates), min(y_coordinates)), (max(x_coordinates), max(y_coordinates))]

def get_pixel_color(x, y):
    ids = canvas.find_overlapping(x, y, x, y)  # This is our bottleneck!

    if ids: # if list is not empty
        index = ids[-1]
        return canvas.itemcget(index, 'fill')

    return 'white' # default color

screen = Screen()
screen.setup(500, 500)
turtle = Turtle(visible=False)
turtle.color('red')

canvas = screen.getcanvas()
width, height = screen.window_width(), screen.window_height()

for iterations in range(1, 7):
    screen.clear()
    turtle.clear()

    screen.tracer(False)

    polygon_start_time = time()
    polygon = koch_snowflake(turtle, iterations, 200)
    polygon_elapsed = round((time() - polygon_start_time) * 1000)  # milliseconds

    screen.tracer(True)

    ((x_min, y_min), (x_max, y_max)) = bounding_box(polygon)
    screen.update()

    # Convert from turtle coordinates to tkinter coordinates
    x1, y1 = floor(x_min), floor(-y_max)
    x2, y2 = ceil(x_max), ceil(-y_min)

    canvas.create_rectangle((x1, y1, x2, y2))

    count = 0

    pixel_count_start_time = time()
    for x in range(x1, x2 + 1):
        for y in range(y1, y2 + 1):
            if get_pixel_color(x, y) == 'red':
                count += 1
    pixel_count_elapsed = round((time() - pixel_count_start_time) * 1000)

    print(iterations, count, polygon_elapsed, pixel_count_elapsed, ((x1, y1), (x2, y2)))

screen.exitonclick()

控制台输出

> python3 test.py
1 23165 1 493 ((-1, -58), (201, 174))
2 26064 4 1058 ((-1, -58), (201, 174))
3 27358 9 1347 ((-1, -58), (201, 174))
4 28159 29 2262 ((0, -58), (201, 174))
5 28712 104 5925 ((0, -58), (201, 174))
6 28881 449 19759 ((0, -58), (200, 174))
> 

字段如下:

  1. 迭代
  2. 像素计数
  3. 以毫秒为单位绘制图像的时间
  4. 以毫秒为单位计算像素的时间
  5. 计算的边界框(在 tkinter 坐标中)

最终迭代屏幕输出

请注意,分形是由 turtle 绘制的,但边界框是由底层 tkinter 绘制的,以验证我们是否正确转换了坐标。

可能的解决方案

找到一种不依赖于find_overlapping() 的方法。我认为接下来要研究的是将画布转换为位图图像并对其进行像素计数,或者首先绘制位图图像。关于将画布直接或间接通过 Postscript 转换为位图的 SO,有几个讨论。然后,您可以加载该图像并使用 Python 图像库之一来计算像素。虽然更复杂,但它应该提供一种对像素进行计数的恒定时间方式。或者,有一些库可以绘制位图,您可以将其加载到 tkinter 中进行视觉验证,然后可以直接进行像素计数。祝你好运!

【讨论】:

    猜你喜欢
    • 2023-01-13
    • 1970-01-01
    • 1970-01-01
    • 2017-08-09
    • 2012-07-01
    • 1970-01-01
    • 2022-12-30
    • 2021-07-24
    • 2016-10-02
    相关资源
    最近更新 更多