Skip to content Skip to sidebar Skip to footer

How To Render Mandelbrot Set Faster?

I'm currently drawing the Mandelbrot set pixel by pixel with PhotoImage and tkinter. I'm using basically the algorithm directly with no modifications. Are there methods to make the

Solution 1:

Setting one pixel at a time is likely the main source of the slowdown. Instead of calling put for each pixel, computer a whole row of pixels, or an entire matrix of pixels, and then call put one time at the end of the loop.

You can find an example here, among other places: https://web.archive.org/web/20170512214049/http://tkinter.unpythonic.net:80/wiki/PhotoImage#Fill_Many_Pixels_at_Once

Solution 2:

Here is my code, it draws a 640x480 Mandelbrot in 8-9 seconds.

It does up to 256 iterations per pixel, uses a color map list, 'puts' only once to PhotoImage and doesn't rely on symetry, so it could show any zoomed area of the set.

It's a pity that Tkinter doesn't allow access to the raster information of PhotoImage as a buffer and that the clumsy string is required.

from tkinter import Tk, Canvas, PhotoImage,NW,mainloop 
from time import clock

defmandel(kx,ky):
  """ calculates the pixel color of the point of mandelbrot plane
      passed in the arguments """global clr
  maxIt = 256
  c = complex(kx, ky)
  z = complex(0.0, 0.0)
  for i inrange(maxIt):
      z = z * z + c
      ifabs(z) >= 2.0:
         return (255-clr[i],0,0)
  return(0,0,0)

defprepare_mdb(xa,xb,ya,yb):
    """ pre-calculates coordinates of the mandelbrot plane required for each
      pixel in the screen"""global x,y,xm,ym
    xm.clear
    ym.clear
    xm=[xa + (xb - xa) * kx /x  for kx inrange(x)]
    ym=[ya + (yb - ya) * ky /y  for ky inrange(y)]


x=640
y=480#corners of  the mandelbrot plan to display  
xa = -2.0; xb = 1.0
ya = -1.5; yb = 1.5#precalculated color table
clr=[ int(255*((i/255)**12)) for i inrange(255,-1,-1)]
xm=[]
ym=[]
prepare_mdb(xa,xb,ya,yb)

#Tk 
window = Tk()
canvas = Canvas(window, width = x, height = y, bg = "#000000")
t1=clock()
img = PhotoImage(width = x, height = y)
canvas.create_image((0, 0), image = img, state = "normal", anchor = NW)
pixels=" ".join(("{"+" ".join(('#%02x%02x%02x' % mandel(i,j) for i in xm))+"}"for j in ym))
img.put(pixels)
canvas.pack()
print(clock()-t1)
mainloop()

enter image description here

Solution 3:

Pure python is not that fast for numeric code. The easiest way to speed things up would be to use PyPy. If that is not fast enough, vectorize your algorithms using numpy. If that is still not fast enough, use Cython, or consider rewriting it in C.

Solution 4:

For a modest increase in speed (but not enough to offset the difference between a compiled language and an interpreted one), you can precalculate some of the values.

Right now, you're calculating DIAMETER / HEIGHT once per inner loop, and CENTER[1] - 0.5 * DIAMETER as well as DIAMETER / WIDTH once per outer loop. Do this beforehand.

len(colors) also won't change and can be replaced by a constant. In fact, I'd probably write that function as

def color(i):
    if i == ITERATIONS:
        return"#000000"else:
        return ("#0000AA", "#88DDFF", "#FF8800", "#000000")[(i//2) % 4]# are you sure you don't want ("#0000AA", "#88DDFF", "#FF8800")[(i//2) % 3] ?

Also, x**2 is slower than x*x (because the x**y operator doesn't shortcut for the trivial case of y==2), so you can speed that calculation up a bit.

Solution 5:

Most time is spent in the inner loop in mandel(). z*z instead of z**2 had a slight effect. There is not much else to speed up there that I can see. Removing constants from other loops had little effect, though I tend to prefer doing so. Choosing ITERATIONS so that ITERATIONS//2 % len(colors) == len(colors)-1, as in 46 //2 % 4 == 3, allows simplification of the code. Exploiting symmetry around the x-axis cuts time in half. Starting imag at 0 avoids the roundoff error of 300 subtractions from +/- DIAMETER / 2 and results in a clean center line in the image.

from tkinter import *

ITERATIONS = 46
WIDTH, HEIGHT = 601, 601  # odd for centering and exploiting symmetry
DIAMETER = 2.5

start = (-.5 - DIAMETER / 2, 0)  # Start y on centerline
d_over_h = DIAMETER / HEIGHT
d_over_w = DIAMETER / WIDTH

def mandel(c):
    z = 0for i in range(ITERATIONS):
        z = z*z + c
        if abs(z) > 2:
            return i     
    return ITERATIONS

root = Tk()
canvas = Canvas(root, width=WIDTH,height=HEIGHT)
canvas.pack()
img = PhotoImage(width=WIDTH, height=HEIGHT)
canvas.create_image(((WIDTH+1)//2, (HEIGHT+1)//2), image=img, state="normal")real, imag = start

colors = ("#0000AA", "#88DDFF", "#FF8800", "#000000")
ncolors = len(colors)
yrange = range(HEIGHT//2, -1, -1)  # up from centerline
ymax = HEIGHT - 1for x in range(WIDTH):
    for y in yrange:
        i = mandel(complex(real, imag))
        color = colors[i//2 % ncolors]
        img.put(color, (x, y))
        img.put(color, (x, ymax - y)) 
        imag += d_over_h
    imag = start[1]
    real += d_over_w

mainloop()

Post a Comment for "How To Render Mandelbrot Set Faster?"