Как получить область формы, заполненную черепахой - PullRequest
0 голосов
/ 14 мая 2019

Я нарисовал фрактальную форму в Python, используя черепаху, и пытаюсь получить площадь этого фрактала после достаточно высокой итерации. Этот фрактал связан со снежинкой Коха, для тех, кто заинтересован.

Я смог заполнить фрактал черным, используя begin_fill () и end_fill (). Затем я использовал этот ответ , чтобы получить цвет каждого пикселя в допустимом диапазоне. Если он не был равен белому, то я добавлял один к счету. Это решение работает для небольшой итерации фрактала. Однако для перехода на более высокую итерацию требуется непомерное количество времени.

Вот мой код для фрактала.

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

Я хочу иметь возможность быстрее находить область этого фрактала. Это занимает большую часть времени не в построении графика фрактала, а в двойном цикле for для x и y. Я думаю, что если есть способ найти область, в то время как черепаха наполняется, это было бы оптимально.

1 Ответ

0 голосов
/ 14 мая 2019

сложность нарисованного изображения не должна влиять на время, необходимое для подсчета черных пикселей

К сожалению, в этом случае это так.Если мы посмотрим на более ранний источник get_pixel_color() кода , мы найдем говорящий текст "медленно".Но что еще хуже, он на самом деле замедляется!

Этот код построен поверх canvas.find_overlapping(), который ищет объекты высокого уровня, расположенные над X, Y.В случае tkinter, заполняющего объект для черепахи, перекрытие , до трех слоев в коде ниже.Это увеличивается, поскольку фактический становится более сложнымВот мой код для демонстрации этого:

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)

ЗАВЕРШЕНИЕ ВЫВОДА ЭКРАНА ЗАКЛЮЧИТЕЛЬНОЙ ИТЕРАЦИИ

enter image description here

Обратите внимание, что фрактал нарисован turtle , ноограничивающий прямоугольник рисуется нижележащим tkinter , чтобы убедиться, что мы правильно преобразовали координаты.

ВОЗМОЖНОЕ РЕШЕНИЕ

Найдите подход, который неположитесь на find_overlapping().Я думаю, что следующая вещь, которую нужно исследовать, это либо преобразовать холст в растровое изображение и подсчитать его по пикселям, либо сначала нарисовать растровое изображение.На SO есть несколько обсуждений о преобразовании холста в растровое изображение, прямо или косвенно через Postscript.Затем вы можете загрузить это изображение и использовать одну из библиотек изображений Python для подсчета пикселей.Хотя и более сложный, он должен обеспечивать постоянный способ подсчета пикселей.Кроме того, есть библиотеки для рисования растровых изображений, которые вы можете загрузить в tkinter для визуальной проверки, но затем можете напрямую подсчитать количество пикселей.Удачи!

...