FireGarden FireGarden - 2 months ago 4
Python Question

Trying to plot Mandelbrot set - no matter what, always get plain black image

First, here's the code I have:

from PIL import Image as im
import numpy as np

def mandelbrot_iteration(c):
iters = 0
while abs(c)<2 and iters<200:
c=c**2+c
iters+=1
return iters

HEIGHT = 400
WIDTH = 500

diag = im.new('L',(WIDTH, HEIGHT))
pix = diag.load()

x_pts = np.arange(-2,2,4/WIDTH)
y_pts = np.arange(-2,2,4/HEIGHT)

for x in x_pts:
for y in y_pts:
pix[x+2,y+2]=mandelbrot_iteration(complex(x,y))

diag.save("Fractal.png")


I thought this was quite straight forward. I see how many interations each point on a grid of complex numbers takes to grow past an abs. value of 2 and plot these values as a colour at each point (with 200 being the cutoff, assuming the sequence doesn't diverge). In the range specified, there should definitely be some non-trivial things going on, but no matter what I try, the image made is plain black.

Also this method of generating images has almost zero documentation. I've searched a lot, and this:


im.load()

Allocates storage for the image and loads it from the file (or from
the source, for lazy operations). In normal cases, you don’t need to
call this method, since the Image class automatically loads an opened
image when it is accessed for the first time.

(New in 1.1.6) In 1.1.6 and later, load returns a pixel access object
that can be used to read and modify pixels. The access object behaves
like a 2-dimensional array, so you can do:

pix = im.load() print pix[x, y] pix[x, y] = value

Access via this object is a lot faster than getpixel and putpixel


Is everything that I can find about it (no examples out there either), which is very frustrating. I imagine the line pix[x+2,y+2] is at fault. The '+2's are there to stop the "out of range" errors, but, having tried some examples, I have no idea what it does with input numbers to generate a colour. I did find that 'L' when the image is created should make a greyscale image, but no idea what range pix[x,y] expects or anything. Everything came out black...

Answer

The immediate problem is your scale is off.

In this line pix[x+2,y+2]=..., with your ranges for x and y, the only pixels that are being drawn are 0..4. Since the last few pixels drawn are black, the entire top left 4x4 square is black (and the rest is 0 – also black – by default, for a new image).

That can be fixed like this:

from PIL import Image as im
import numpy as np 

def mandelbrot_iteration(c): 
    iters = 0
    while abs(c)<2 and iters<200:
        c=c**2+c
        iters+=1
    return iters

HEIGHT = 400
WIDTH = 500

diag = im.new('L',(WIDTH, HEIGHT))
pix = diag.load()

x_pts = np.arange(-2,2,4.0/WIDTH)  
y_pts = np.arange(-2,2,4.0/HEIGHT)

for x in x_pts:
    for y in y_pts:
        pix[WIDTH*(x+2)/4.0,HEIGHT*(y+2)/4.0]=mandelbrot_iteration(complex(x,y))

diag.show()

although the result is not yet a good Mandelbrot...

mandelsmorgasbord

With hcs' comment "mandelbrot iteration should be z=0, while abs(z)<2, z=z**2+c" applied, you'd use this code

from PIL import Image as im
import numpy as np 

def mandelbrot_iteration(c): 
    iters = 0
    z = 0
    while abs(z)<2 and iters<200:
        z=z**2+c
        iters+=1
    return iters

HEIGHT = 400
WIDTH = 500

diag = im.new('L',(WIDTH, HEIGHT))
pix = diag.load()

x_pts = np.arange(-2,2,4.0/WIDTH)  
y_pts = np.arange(-2,2,4.0/HEIGHT)

for x in x_pts:
    for y in y_pts:
        pix[WIDTH*(x+2)/4.0,HEIGHT*(y+2)/4.0]=mandelbrot_iteration(complex(x,y))

# diag.show()
diag.save("Fractal.png")

and lo and behold, a true Mandelbrot pops up:

Correct Mandelbrot