'''
Created on Feb 17, 2015
@author: Brett Paufler
Copyright Brett Paufler
With commentary like this,
it has to have been released to the wild
word for word...
'''
#Copyright Brett Paufler
#Released Under the Exclusive
#For Information Purposes ONLY (ALL RIGHTS RESERVED) License
import numpy as np #because that's how the cool kids do it
import skimage.io
def base_iter(x,c):
'''This is the Heart of solving the Mandelbrot Set
Full numpy arrays will be fed into this function
'''
return x*x + c
def main_iter(c,numIt=50):
'''Returns the Mandelbrot Set
c is a numpy array of complex numbers defined over
the area of interest
numIt is the number of iterations, more, better
but there is a point of diminishing returns
'''
#This x (and this c) are conceptually the same,
# as per base_iter above
x = np.zeros_like(c)
for _ in range(numIt):
x = base_iter(x, c)
#If the absolute value of (a+bi) ever exceeds 2
#The point is not part of the Mandelbrot Set
#And tends toward infinity
x = abs(x)
x[x>2] = np.nan #Most of these values are already np.nan
x[x<=2] = 1
x[np.isnan(x)] = 0
return x
def i_coord(s, (uL,lR)):
'''returns a complex coordinate grid (aka c in the above)
size = (s,s) = s high, s wide
min_max is passed in a tuple of two complex numbers
uL is the min (Upper Left)
lR is the max (Lower Right)
Odd fact, I am right/left dyslexic
Often heard around my house,
'No! Your other right!'
'''
#real part, as a row
r = np.linspace(np.real(uL), np.real(lR), s, dtype=complex)
#imaginary part, as a column
im = np.linspace(np.imag(uL), np.imag(lR), s, dtype=complex)
im = np.reshape((im * 1j), (s,1)) #turns into a column, essentially
#add a row and a column together in Numpy
#get a Matrix, row wide, column high
return (r+im)
def next_coor(s, mandebrot, c):
'''Returns the area of 'Most Interest' in the Mandelbrot Array
Most interest being defined as relatively even numbers of 0's & 1's
s = size (s,s) of image out, as it does everywhere else
mandebrot (misspelled, of course) is the picture image array
c is the starting complex array (for this go round, this image)
'''
#t being the value of 50-50 0's & 1's
t = s*s/8.0
m = []
#This is messy
#It's the type of hell that's usually easier for me
# to rewrite than debug
m.append((abs(t - sum(sum(mandebrot[:s/2, :s/2]))),
(np.min(c[:s/2, :s/2]), np.max(c[:s/2, :s/2]) )))
m.append((abs(t - sum(sum(mandebrot[:s/2, s/2:]))),
(np.min(c[:s/2, s/2:]), np.max(c[:s/2, s/2:]) )))
m.append((abs(t - sum(sum(mandebrot[s/2:, :s/2]))),
(np.min(c[s/2:, :s/2]), np.max(c[s/2:, :s/2]) )))
m.append((abs(t - sum(sum(mandebrot[s/2:, s/2:]))),
(np.min(c[s/2:, s/2:]), np.max(c[s/2:, s/2:]) )))
#sort, take the first (lowest t-sum),
# return the second part of the tuple
m.sort()
_, (min_max) = m[0]
return min_max
def auto_zoom(s, n, min_max=(-2.0 +2.0j, 2.0 -2.0j)):
'''Successive Mandelbrot's with Magnification
s = image size
n = number of successive images (blurs out at 20 or so
min_max is the starting area of interest
for a bunch of size options (20 images each)
for s in [100, 250, 500, 1000]:
min_max = (-2.0 +2.0j, 2.0 -2.0j)
auto_zoom(s,20,min_max)
'''
#1 loop per image out
for n in range(1,n):
print "\nStarting Loop"
print "Min Max: %d, %r" % (s, min_max)
c = i_coord(s, min_max) #c array for current coordinates
mandebrot = main_iter(c) #the main computation
sN = "mandebrot_{:0>4}_{:0>4}.png".format(s,n)
print "Saving %s" % sN
skimage.io.imsave(sN, mandebrot) #save the image
min_max = next_coor(s, mandebrot, c) #reset coordinates
def main_seq_picture_iter(s,
numPics=12,
min_max=(-2.0 +2.0j, 2.0 -2.0j),
intermediate=True):
'''saves a series of images,
whereas autozoom focuses in (drills ever deeper)
into the Mandelbrot Set,
This function outputs a series of ever refined images
focusing on a single area of the set
s = size of pictures to save
numPics = number of sequential images
(iterations, refinement of the Mandelbrot set)
min_max is tuple of area of interest
so by manually (or by other means)
the images may be obtained for any section of the set
intermediate = whether to save the 'part way' image progressions
(rather than just the final product)
Probably rather than a function call,
one should think of this as a script.
It solves a problem (pictures for this webpage).
But to solve another problem,
it would need to be rewritten or tweaked
(and not just passed different parameters)
'''
#c = array of complex numbers
#target of Mandelbrot image area
c = i_coord(s, min_max)
#g = for the compound image out
# green and grayscale
g = np.zeros((s,s))
#x is the incremented array
# the increased resolution copy of the Mandelbrot set
# as per other functions above (same x, essentially)
x = np.zeros_like(c)
for n in range(1, numPics + 1):
x = base_iter(x, c)
#p = binary (black/white) (0,1)
# color scale image for each iteration
# Note the logic, similiar to main_iter()
p = x.copy()
p = abs(p)
p[p>2] = np.nan
p[p<=2] = 1
p[np.isnan(p)] = 0
#increments the soft output, grayscale array
# Since this is an image effect
# p*n, p+n, p by itself, whatever
g += p * n
if intermediate: #save a PNG
sN = "mandebrot_resolve_{:0>4}_{:0>4}.png".format(s,n)
skimage.io.imsave(sN,p)
print sN
#After the Loop, Output some Compound Images
z = np.zeros_like(g, dtype=int)
o = np.ones_like(g, dtype=int) * 255
sN = "mandebrot_full_resolve.png" #white circle, gray Mandelbrot
skimage.io.imsave(sN,g) #one layer, so grayscale
sN = "mandebrot_full_resolve_green.png" #green circle
skimage.io.imsave(sN,np.dstack((z,g,z,g))) #(R,G,B, Alpha)
g = g / np.max(g) * 255
print np.max(g), np.min(g)
g = np.array(g, dtype=int)
sN = "mandebrot_full_resolve_green_%s.png" % s
skimage.io.imsave(sN, np.dstack((z,g,z,o))) #not used
print sN
sNa = "mandebrot_full_resolve_green_alphatrack_%s.png" % s
skimage.io.imsave(sNa, np.dstack((z,g,z,g))) #Green Mandelbrot
print sNa
return s
main_seq_picture_iter(1000, 12)
'''
Originally, I had it on my TODO list to do a colourized version
of the Mandelbrot Set (Using some sort of compound logic),
but I'm not convinced the colours mean anything (are just random),
so I've sort of lost my enthusiasm for the project.
Still, someday...
'''