0

I have the following Python program that endeavours to use the normalized iteration count algorithm to colour the Mandelbrot set:

from PIL import Image
import numpy as np
from matplotlib.colors import hsv_to_rgb

steps = 256 # maximum iterations
bailout_radius = 64 # bailout radius

def normalized_iteration(n, abs_z):
    return n + 1 - np.log2(np.log(abs_z))/np.log(2)

def make_set(real_start, real_end, imag_start, imag_end, height):

    width = \
        int(abs(height * (real_end - real_start) / (imag_end - imag_start)))

    real_axis = \
        np.linspace(real_start, real_end, num = width)
    imag_axis = \
        np.linspace(imag_start, imag_end, num = height)
    complex_plane = \
        np.zeros((height, width), dtype = np.complex_)

    real, imag = np.meshgrid(real_axis, imag_axis)

    complex_plane.real = real
    complex_plane.imag = imag

    pixels = \
        np.zeros((height, width, 3), dtype = np.float_)

    new = np.zeros_like(complex_plane)
    is_not_done = np.ones((height, width), dtype = bool)

    # cosine_interpolation = lambda x: (np.cos(x * np.pi + np.pi) + 1) / 2
    
    for i in range(steps):
        new[is_not_done] = \
            new[is_not_done] ** 2 + complex_plane[is_not_done]
        
        mask = np.logical_and(np.absolute(new) > bailout_radius, is_not_done)
        pixels[mask, :] = (i, 0.6, 1)
        is_not_done = np.logical_and(is_not_done, np.logical_not(mask))

    new_after_mask = np.zeros_like(complex_plane)
    new_after_mask[np.logical_not(is_not_done)] = \
        new[np.logical_not(is_not_done)]
    new_after_mask[is_not_done] = bailout_radius

    pixels[:, :, 0] = \
        normalized_iteration(pixels[:, :, 0], np.absolute(new_after_mask)) / steps

    image = Image.fromarray((hsv_to_rgb(np.flipud(pixels)) * 255).astype(np.uint8))

    image.show()

make_set(-2, 1, -1, 1, 2000) 

It produces a fairly nice image. However, when I compare it to other sets employing this algorithm, the colours in my set barely change. If I reduce steps, I get a more varied gradient, but that reduces the quality of the fractal. The important parts of this code are my normalized_iteration definition, which varies slightly from this Wikipedia article's version,

def normalized_iteration(n, abs_z):
    return n + 1 - np.log2(np.log(abs_z))/np.log(2)

where I use that definition (mapping the function to the array of pixels),

pixels[:, :, 0] = \
        normalized_iteration(pixels[:, :, 0], np.absolute(new_after_mask)) / steps

and the final array, where I convert the HSV format to RGB and turn the pixel values on [0, 1) to values on [0, 255)

image = Image.fromarray((hsv_to_rgb(np.flipud(pixels)) * 255).astype(np.uint8))

I have been fighting with this problem for a while now, and I am not sure of what is going wrong. Thanks for helping me determine how to make the gradient more varied in colour and for bearing with my perhaps hard-to-read code. Also, I realize that there is room for optimization in there.

  • Just to be sure, your problem is, _"However, when I compare it to other sets employing this algorithm, the colours in my set barely change."_? If so, please provide some of those _"other sets"_, such that people here can get an impression, what your desired output is. – HansHirse Mar 15 '21 at 07:56
  • The problem was whether I was employing the algorithm correctly. I asked the question seeking a more varied colour gradient, so I wondered whether I was missing something or did something wrong; for instance, maybe I need a distance estimator to get a more distinct colour gradient, or maybe I need to change a parameter somewhere. If you need a visual of what a noticeable colour gradient in the Mandelbrot set looks like, see http://www.stefanbion.de/fraktal-generator/colormapping/example1_sinusoid_interpolation.png. – Jack DeSerrano Mar 15 '21 at 15:28

0 Answers0