c++mandelbrotcimg

Can't get color smoothness to work with mandelbrot set using CImg


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


Solution

  • 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 );       
    }