Julia Set in Julia - Part 2

In this post I want to show you how to create beautiful pictures of a Julia set.

Also this time we will make use of the most loved language of this site: Julia ❤️

Let’s start immediately with the installation of the main package that we will use: Images.

So let’s open our Julia REPL, hit the ] key to enter in package mode and type following command

1
pkg> add Images

Once our package is installed, let’s dust off the code for the Julia set from my old post. (If you haven’t read it yet, it won’t make sense for you to continue with this post)

However the code was something like the following

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const c = -0.79 + 0.15im

f(z::Complex) = z^2 + c
f(z::Real) = f(z + 0im)

const R = 2
const ε = 0.016
const symbols = " .:-=+*#%\$@"

for x=-R:ε:R
  for y=-R:ε:R
    z::Complex = complex(y,x)

    k = 1
    n = 100

    while abs(z)  (2)*R && (k += 1) < n
      z = f(z)
    end
    print(symbols[convert(Int64, ceil(k/11))])
  end
  println()
end

First, we no longer want to print ASCII symbols on the screen, so we no longer need the symbols variable and the print function.

What we want to do is instead draw pixels on an image!

In Julia (as in any other language) an image is represented internally in the code as a matrix of pixels. In turn, a pixel is represented by a triple of numerical values, called RGB: Red-Green-Blue values. The RGB color model is an additive color model in which the red, green, and blue primary colors of light are added together in various ways to reproduce a broad array of colors. If you want to know more, visit the relative wikipedia page.

By importing the Images package we will have a structure available to represent and manage a triple RGB. Needless to say, this structure is called RGB, and it has three attributes:

  • a value between 0 and 1 indicating the red channel
  • a value between 0 and 1 indicating the green channel
  • a value between 0 and 1 indicating the blue channel
1
2
3
4
5
6
7
RGB(1.0, 0.0, 0.0) # Red color
RGB(0.0, 1.0, 0.0) # Green color
RGB(0.0, 0.0, 1.0) # Blue color

RGB(1.0, 1.0, 1.0) # White color
RGB(0.5, 0.5, 0.5) # Gray color
RGB(0.0, 0.0, 0.0) # Black color

Observe that when the three channels, R-G-B, take on the same value, a color is obtained in the gray scale. In particular, when the values are all three 0s then we get the color black, while when they are all three 1s we get the color white.

To simplify the work, the Images package offers us the Gray type, which accepts a single attribute: the brightness level.

1
2
3
Gray(0.0) # = RGB(0.0, 0.0, 0.0)
Gray(0.3) # = RGB(0.3, 0.3, 0.3)
Gray(1.0) # = RGB(1.0, 1.0, 1.0)

We are then ready to create an “empty image” (ie all black)

1
image = [Gray(0.0) for _ = -R:ε:R, _ = -R:ε:R]

We thus obtain a balck pixel matrix, where each pixel represents a point of the $\left[ -R, R \right]^2$ square.

At this point, we’re simply going to increase the brightness of the pixels where the points have a value of $k$ greater, being careful to normalize with respect to $n$.

1
image[i, j] = Gray(k / n)

WARNING: remember to always keep track of the row and column indexes $i, j$, which are different from the coordinates $x, y$ of the points on the plane!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const ε = 0.001
const range = -R:ε:R

image = [Gray(0.0) for _ = range, _ = range]

i, j = 1, 1

for x = range
    j = 1
    for y = range
        z::Complex = complex(y, x)

        k = 1
        n = 100

        while abs(z)  (2) * R && (k += 1) < n
            z = f(z)
        end

        image[i, j] = Gray(k / n)

        j += 1
    end
    i += 1
end

To save the image just call the save function (also offered by our wonderful Images package), passing it as parameters the filename and the pixel matrix.

1
save("~/julia_set.png", img)

The resulting image will be something like this

c = -0.75 + i0.150522

If you want to get the negative of this image, just reverse the brightness value

1
image[i, j] = Gray(1 - k / n)

thus obtaining

c = -0.75 + i0.150522

If you want you can also play with the RGB parameters instead of the brightness, thus obtaining beautiful plays of colors.

Have fun with your imagination!


Post Scriptum

This page contains a reference to a particular moment. Hopefully the person related to this reference will sooner or later find it.

Although looking at the content of my articles, I don’t think the concerned will ever read one, let alone find a hidden detail …

However, despite my being always hyper-realistic, this time I like to dream.