-2

So in a step to learn C++ i decided to try to implement the mandelbrot set using CImg. Here is my program:

#include <cstdlib>
#include <iostream>
#include <complex>

#define cimg_use_png
#include "CImg.h"

using namespace cimg_library;

const int MAX_ITER = 80;
const int WIDTH = 600;
const int HEIGHT = 400;
const int DEPTH = 1;
const int COLOR_SPECTRUM = 3;

const int RE_START = -2;
const int RE_END = 1;
const int IM_START = -1;
const int IM_END = 1;

double mandelbrot(const std::complex<double> &value) {
    std::complex<double> z(0, 0);
    int n = 0;
    while (abs(z) <= 2 && n < MAX_ITER){
        z = (z * z) + value;
        n += 1;
    }

    if(n == MAX_ITER) {
        return n;
    }

    return n + 1 - log(log2(norm(z)));
}

int main() {
    CImg<int> img(WIDTH, HEIGHT, DEPTH, COLOR_SPECTRUM, 0);
    img.RGBtoHSV();

    cimg_forXY(img, x, y) {
        std::complex<double> complex(RE_START + ((double)x / WIDTH) * (RE_END - RE_START),
                                     IM_START + ((double)y / HEIGHT) * (IM_END - IM_START));
        double m = mandelbrot(complex);

        int hue = (255 * m / MAX_ITER);
        int saturation = 255;
        int value = 0;
        if (m < MAX_ITER) {
            value = 255;
        }

        int color[] = { hue, saturation, value };
        img.draw_point(x, y, color);
    }

//    img.HSVtoRGB().display("Mandelbrot");
    img.HSVtoRGB().save_png("output/test.png");

    return 0;
}

I manage to draw out the classic mandelbrot shape, but then i tried implementing smoothing to get rid of the ugly banding. But i cant get it to really work.

And seriously tbh i have no idea what i'm doing because math is hard.

This is what i get Pink mandelbrot set

I also get this warning when its saving the image

[CImg] *** Warning ***[instance(600,400,1,3,0x104800000,non-shared)] 
CImg<int>::save_png(): Instance has pixel values in 
[-1.65164e+07,65025], probable type overflow in file 'output/test.png'.

so apparently i have an overflow somewhere but i can't really tell where.

If anyone could help me and explain what I'm doing, and point out all the wrong things i have done but in a simple way, i would be forever grateful.

UPDATE

so by adding a normalization step i managed to get a better image:

img.HSVtoRGB()
    .normalize(0, 255)
    .save_png("output/test.png");

And the overflow error is gone.

Mandelbrot gradient

But there seems to be something wrong in the conversion between HSV and RGB.

Everything that is white should be black.

The HSV values i'm writing are 255, 255, 0

but it gets converted to 255, 255, 255 in RGB which is wrong. It should get converted to black.

UPDATE 2

As Bob informed me Hue and Saturation is not a number between 0..100 it's represented by a number between 0..1.

That was my main misstake and why i got an overflow and the blacks became white. By correcting that and doing the small fixes Bob suggested i now get:

Correct Mandelbrot

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • Debug what sort of values you're stuffing into `color`. – tadman Jul 04 '19 at 21:14
  • OT: Please note that you could use [std::log2](https://en.cppreference.com/w/cpp/numeric/math/log2) and [std::norm](https://en.cppreference.com/w/cpp/numeric/complex/norm). – Bob__ Jul 04 '19 at 21:18
  • the smothing is `n + 1 - log(log(abs(z))/log(2))/log(2)` part but i always just get this banding, that i showed in the image, and i don't know why. – Toerktumlare Jul 04 '19 at 21:20
  • @Bob oh yeah, thanks didn't know of the `log2` – Toerktumlare Jul 04 '19 at 21:21
  • Are you trying to get something like this http://www.fractalforums.com/mandelbrot-and-julia-set/coloring-points-inside-the-mandelbrot-set/ or https://stackoverflow.com/questions/369438/smooth-spectrum-for-mandelbrot-set-rendering ? – Bob__ Jul 04 '19 at 21:31
  • 1
    @Tadman i have debug and i get all positive values between 0-255. I think it has to do something with the color depth and/or the conversion between HSV and RGB color space – Toerktumlare Jul 04 '19 at 21:35

1 Answers1

2

In the HSV color model, the hue is usually expressed as an angle in degrees [0°, 360°), while the saturation and value are in the range [0, 1].

A way to fix the posted code is to adjust the values and their type stored in img.

CImg<double> img(WIDTH, HEIGHT, DEPTH, COLOR_SPECTRUM, 0);
//   ^^^^^^ I guess a float could be enough
// img.RGBtoHSV();  Here, is useless. CImg has no notion of the color space

cimg_forXY(img, x, y)
{
    std::complex<double> c(/* Get real and imaginary part from x and y */);

    double m = mandelbrot(c);

    double color[] = {
//  ^^^^^^    
        360.0 * m / MAX_ITER,       // hue
        1.0,                        // saturation
        (m < MAX_ITER) ? 1.0 : 0.0  // value
    };

    img.draw_point(x, y, color);
}

img.HSVtoRGB().normalize(0, 255).save_png("output/test.png");

Also note that in mandelbrot() you could use

while (std::norm(z) <= 4 && n < MAX_ITER) // 
{   // ^^^^^^^^^^^^^^^^^ To avoid a sqrt at every iteration

    // See e.g. https://linas.org/art-gallery/escape/smooth.html for this:
    return n + 1 - std::log2( std::log( std::norm(z) ) * 0.5 );       
}
Bob__
  • 12,361
  • 3
  • 28
  • 42
  • that was it! i knew that hue was a value between 0 and 360 but i didn't know that saturation and value was a value between 0..1! i thought it was 0..100! I learnt something new today! you tha man @Bob__ – Toerktumlare Jul 05 '19 at 17:31