Welcome to Fractal Forums

Fractal Software => Programming => Topic started by: Matplotlib on April 24, 2017, 01:00:04 PM




Title: Julia smooth-colouring : how to do ?
Post by: Matplotlib on April 24, 2017, 01:00:04 PM
Hello,

I am running a Julia set program, and I have some issues... I don't want to enter in the language's details, it's not what's wrong.
I run z\mapsto z^2+c several times, till I escape or I get to the max number of iterations possible. During the process, I sum over all the exp(-|z|) values (is it the good way for smooth-colouring ?), that I store in a var (let's name it "escape_count"). In the end, I get a number that I don't know how to use ! How to get a color from the value of escape_count ?

Any examples with explanations would be very helpful, even if you redirect to some papers ! (I haven't been able to find something clear about the smooth-colouring)


Title: Re: Julia smooth-colouring : how to do ?
Post by: lycium on April 24, 2017, 04:16:03 PM
Welcome to the forum :) Some resources I've found on this topic a few years ago:

http://www.iquilezles.org/www/articles/mset_smooth/mset_smooth.htm
http://linas.org/art-gallery/escape/escape.html

Also there is even a master's thesis on this topic by Jussi Härkönen: http://jussiharkonen.com/gallery/coloring-techniques/


Title: Re: Julia smooth-colouring : how to do ?
Post by: xenodreambuie on April 24, 2017, 10:09:12 PM
That is said to be a good smooth colouring method, though I haven't tried it. The most general way to map a scalar to colours is to multiply it by a strength (or density) parameter and add an offset parameter. Then wrap it into a fixed range as an index into a gradient. The alternative is to generate colour values algorithmically, typically to make some kind of endlessly varying combination of waves.


Title: Re: Julia smooth-colouring : how to do ?
Post by: FractalStefan on April 24, 2017, 11:52:31 PM
Hi Matplotlib,

in your Iterate function, where you do the Z->Z²+C iteration, just do the following before you return the iterator value (it):

Code:
it = it + 2 - log(log(a² + b²)) / log(2)

it is the iterator value, and a and b are the real and imaginary part of Z after Z escaped to infinity.

For maximum speed, you can define log(2) as a constant.

Your color mapping code should of course be able to cope with a fractional iterator value.

The escape radius should be greater than the usual 2, e.g. 4 or 5, to get best results.

Example script (http://www.stefanbion.de/fraktal-generator/mandelbeispiel.htm) (Mandelbrot, but should also work for Julia since they both use the same iteration equation). Click the checkbox to see the difference.

Stefan


Title: Re: Julia smooth-colouring : how to do ?
Post by: lycium on April 24, 2017, 11:57:08 PM
For maximum speed, you can define log(2) as a constant.

Pretty sure maximum speed requires 1/log(2), not log(2) ;)

Furthermore pretty much every compiler will precompute log(2), but won't do the 1/log(2) optimisation.


Title: Re: Julia smooth-colouring : how to do ?
Post by: FractalStefan on April 25, 2017, 12:25:35 AM
Pretty sure maximum speed requires 1/log(2), not log(2) ;)

Because multiplying is faster than dividing?

I made a quick test in JavaScript (http://www.stefanbion.de/tmp/divmultest.htm). The result:

Division: 1204 ms
Multiplication: 1203 ms

Really a big difference! ;-)

Edit: But you're right - at least in JavaScript there's no difference if log(2) is defined as constant or used directly as operand:

Quote
Using a constant as operand

Division: 1205 ms
Multiplication: 1202 ms

Using log(2) as operand

Division: 1203 ms
Multiplication: 1204 ms

Edit2: Okay, I was wrong, multiplication is faster than division. JavaScript tricked me as it optimized the testing loop. Apparently it saw that I did nothing with the result, so I changed the script (here's version 2 (http://www.stefanbion.de/tmp/divmultest2.htm)) so that it now outputs the results - and indeed, the diffrences are now significant:

Quote
Using a constant as operand

Division: 19465 ms (result: 721337883.3478911)
Multiplication: 13629 ms (result: 721369432.0115957)

Using log(2) as operand

Division: 20261 ms (result: 721312455.102812)
Multiplication: 13846 ms (result: 721337110.8961784)

So big difference between mul and div, but not between using log(2) as a constat or directly... 88)

Edit2: Wow, Opera and Chrome are really slow compared to Firefox which I used for the above results!

Opera:

Quote
Using a constant as operand

Division: 32267 ms (result: 721345943.255802)
Multiplication: 29262 ms (result: 721320344.2994019)

Using log(2) as operand

Division: 31125 ms (result: 721339741.0393113)
Multiplication: 28634 ms (result: 721362340.1294624)

Chrome:

Quote
Using a constant as operand

Division: 28945 ms (result: 721352593.7384236)
Multiplication: 26304 ms (result: 721348815.09295)

Using log(2) as operand

Division: 28062 ms (result: 721325577.7421819)
Multiplication: 26247 ms (result: 721343337.9390984)


Title: Re: Julia smooth-colouring : how to do ?
Post by: lycium on April 25, 2017, 06:27:16 AM
lol, "performance" testing with JavaScript :D

You have two "Edit2" btw ;)


Title: Re: Julia smooth-colouring : how to do ?
Post by: Matplotlib on April 26, 2017, 09:13:18 PM
@lycium the PhD is just amazing ! Explains everything in details ! In fact, I had already been on the other links you gave to me, and I did not quite understand properly what they wanted to do.

@FractalStefan thanks ! Also, thanks for the speed comparisons ! I never thought to store 1/ln(2) in a var, which appears to be a simple but great idea (and yes, I use ln for log_e !)


Title: Re: Julia smooth-colouring : how to do ?
Post by: Matplotlib on May 10, 2017, 05:42:40 PM
Okay ! I've read that PhD, it's just as great as I expected ! I looove when things are detailed and as clear as mountain water !

Hence, I've generated by myself some images :
+ http://matplotlib.deviantart.com/art/Julia-set-SIC-algorithm-c-0-5-0-25i-679827575
+ http://matplotlib.deviantart.com/art/Julia-set-SIC-algorithm-c-0-75-0-11i-679827809?ga_submit_new=10%3A1494428357
+ http://matplotlib.deviantart.com/art/Julia-set-SIC-algorithm-c-0-4-0-6i-679829631?ga_submit_new=10%3A1494429324
Let me copy/paste the info I gave on the images' page :
Smooth iteration count : u(z0)=n+1+logd{ln(M)/ln|zn|} where n is the number of iterations, M the maximum radius bound, and d the degree of equation : zn+1=aznd+Q(zn) where deg(Q)<=d-1

Now, the palette I use is defined as follows :
+ r=⌊u(z0)/nmax
+ g,b = r,r

This means I have a palette that has length 256, and palette[i]=i for i∈[|0,255|]. But here are some issues I get :
+ u(z0) appears to never reach nmax. Therefore, I never get r greater than a bouded value (usually around 150). What is the maximum bound of this function u I'm using ? I have tried using the famous u(z0)=n+1-logd{|zn|}, but this does not change much the values taken.
+ How to use an adapted coloured palette ? It looks like the values taken by u get stuck on one side of the palette, that side depending on the current fractal. I have no clue of how to generate beautiful images using all the colours on the palette. Instead, it only uses half the palette.

Here's a palette I'm using (.python/numpy code) :
Code:
palette_r = list(map(lambda z: int(z),np.linspace(0,90,538).tolist() +np.linspace(90,251,428).tolist() +np.linspace(251,251,544).tolist()+np.linspace(251,130,542).tolist()))
palette_g = list(map(lambda z: int(z),np.linspace(0,233,538).tolist()+np.linspace(233,222,428).tolist()+np.linspace(222,0,544).tolist()  +np.linspace(0,0,542).tolist()))
palette_b = list(map(lambda z: int(z),np.linspace(140,0,538).tolist()+np.linspace(0,2,428).tolist()    +np.linspace(2,4,544).tolist()    +np.linspace(4,127,542).tolist()))
It looks like this :
(https://img15.hostingpics.net/pics/547076palette.png)

Now, let's generate the Julia set for the value c=0.5+0.25i using this palette (smaller size) :
(https://img15.hostingpics.net/pics/525468Julia05025jsmall.png)

I'm basically calling palette_X[I] for I=⌊L*u/nmax⌋ where L=len(palette_X). As you can notice, the render is not that good, as a lot of the image is coloured blue.
Also, another issue is that sometimes, u/nmax can exceed 1, so I have an "out of range" error raised. That can be easily fixed by having the palette looping itself and using a modulo, but I just don't get why it behaves like this, as I noticed that u never reached values higher than nmax (I had no problems using the B&W palette).


Title: Re: Julia smooth-colouring : how to do ?
Post by: FractalStefan on May 14, 2017, 12:59:08 AM
In order to see more of the palette colors, you can use a nonlinear function to stretch the colors at the higher end of the iteration count and compress them at the lower end.
In -> this thread (http://www.fractalforums.com/programming/non-parametric-color-mapping-techniques/msg101580/#msg101580) I described some formulas for this purpose.


Title: Re: Julia smooth-colouring : how to do ?
Post by: Matplotlib on May 15, 2017, 08:18:59 PM
Okay, I'm now using the hyperbolic tangent as a non-linear index for the palette. I also noticed I need a "smooth palette", which the linear interpolation does not provide. I'm now using a Bezier interpolation, and I got this render : http://matplotlib.deviantart.com/art/Julia-set-SIC-algorithm-c-0-4-0-6i-680773601 (details in description)

Most of my issues are now solved, thanks !


Title: Re: Julia smooth-colouring : how to do ?
Post by: FractalStefan on May 15, 2017, 09:11:44 PM
Okay, I'm now using the hyperbolic tangent as a non-linear index for the palette.

Interesting idea - I have to try this, too! :)

However - since tanh(x) only approximates the value of 1, you should use

i = tanh(n * u/Nmax) / tanh(n)

with n = ]0,10] (for example 0.1 ... 10)

Also, if you set n by a slider control (or some other input control), the user can set the linearity of the gradient individually.

I also noticed I need a "smooth palette", which the linear interpolation does not provide. I'm now using a Bezier interpolation, and I got this render : http://matplotlib.deviantart.com/art/Julia-set-SIC-algorithm-c-0-4-0-6i-680773601 (details in description)

Nice! Can you explain this more detailed (Bezier interpolation)?


Title: Re: Julia smooth-colouring : how to do ?
Post by: Matplotlib on May 15, 2017, 10:10:20 PM
Nice! Can you explain this more detailed (Bezier interpolation)?
I'll make a more detailed thread tomorrow with the the computer as I already thought about creating one :)


Title: Re: Julia smooth-colouring : how to do ?
Post by: FractalStefan on May 16, 2017, 01:18:34 AM
I'll make a more detailed thread tomorrow with the the computer as I already thought about creating one :)

Very good! :)

And I tried the hyperbolic tangent function meanwhile. The results are included in the comparision chart in my new article about color mapping methods:

http://www.stefanbion.de/fraktal-generator/colormapping/

BTW: I don't use a predefined color palette; instead the actual colors are calculated dynamically by interpolation. For example, if color 1 is #008000 and color 2 is #FF0080, then with a normalized color index of 0.5, the resulting color will be #7F4040. This way the colors automatically become smoothed (when using a fractional iteration count) . Is this similar to what you are doing, except that you are using this Bezier thing instead of linear interpolation as I do?


Title: Re: Julia smooth-colouring : how to do ?
Post by: Matplotlib on May 16, 2017, 11:58:55 AM
You won't believe it but the color-mapping I prefer amongst all the ones you tried is the tanh :)

Anyways, here's what we talked about yesterday evening : http://www.fractalforums.com/programming/creating-a-good-palette-using-bezier-interpolation/