0
votes

I am trying to write code that makes an image of the Mandelbrot fractal in Python 3. The code was working without use of numpy arrays but slowly. To speed it up, I've tried using numpy and numba.

When using Image.fromarray() in PIL on a numpy array of 3-tuples, the resulting image is a series of vertical lines rather than the expected Mandelbrot image. After some research, I think the issue is with data types and possibly with signed versus unsigned integers. I can get things to work if I store ints instead of 3 tuples for the HSV values in the numpy array. Unfortunately, this gives a black and white image and I want a colourful image. The other strange thing is that the image the code produces changes slightly each time I run it. I'm not sure if that is a related or separate issue. Here's code, adjusted to remove the mandelbrot generator and simply create a gradient image, that shows the problem:

from PIL import Image, ImageDraw
from numba import jit
import numpy as np 

@jit
def simple_image(width,height):
    n3 = np.empty((width, height), dtype=object)
    for i in range(width):
        for j in range(height):
            n3[i, j] = (min(j, 255), 255, 255)
    return n3 

arr = simple_image(800, 600) 

im = Image.new('HSV', (800, 600), (0, 0, 0))
im = Image.fromarray(arr.astype(object), mode='HSV')
im.convert('RGB').save('output.png', 'PNG')

Here is the image that is generated. Vertical Lines

When I make a few changes to the code so that it stores ints and creates a black and white image, it works:

from PIL import Image, ImageDraw
from numba import jit
import numpy as np 

@jit
def simple_image(width,height):
    n3 = np.empty((width, height))
    for i in range(width):
        for j in range(height):
            n3[i, j] = min(j, 255)
    return n3 

arr = simple_image(800, 600) 

im = Image.new('HSV', (800, 600), (0, 0, 0))
im = Image.fromarray(arr)
im.convert('RGB').save('output.png', 'PNG')
1
A color image should be 3d, e.g. (width, height, 3) shaped.hpaulj
Are you suggesting that the line im = Image.new('HSV', (800, 600), (0, 0, 0)) should be changed to im = Image.new('HSV', (800, 600, 3), (0, 0, 0)) ? Doing so gives "ValueError: Size must be a tuple of length 2" but I can't figure out what else your comment would mean.Yaacov Iland
Ah, I've got it now, thank you! It's the numpy array that needs to be 3d, instead of a 2d array of tuples.Yaacov Iland

1 Answers

2
votes

Here is code that answers the question thanks to a suggestion from hpaulj. The numpy array was changed from a 2d array of 3-tuples to a 3d array with the third dimension of length 3. dtype was set to 'uint8' in two places.

from PIL import Image, ImageDraw
from numba import jit
import numpy as np 

@jit
def white_image(width,height):
    n3 = np.empty((width, height, 3), dtype='uint8')
    for i in range(width):
        for j in range(height):
            n3[i, j, 0] = min(j, 255)
            n3[i, j, 1] = 255
            n3[i, j, 2] = 255
    return n3 

arr = white_image(800, 600) 

im = Image.new('HSV', (800, 600), (0, 0, 0))
im = Image.fromarray(arr.astype('uint8'), mode='HSV')
im.convert('RGB').save('output.png', 'PNG')