Не удается получить плавность цвета для работы с набором Мандельброта с помощью CImg - PullRequest
0 голосов
/ 05 июля 2019

Итак, на шаге по изучению C ++ я решил попробовать реализовать набор mandelbrot с использованием CImg. Вот моя программа:

#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;
}

Мне удается нарисовать классическую форму Мандельброта, но затем я попытался реализовать сглаживание, чтобы избавиться от уродливой полосы. Но я не могу заставить его действительно работать.

А если серьезно, я понятия не имею, что я делаю, потому что математика сложна.

Это то, что я получаю Pink mandelbrot set

Я также получаю это предупреждение при сохранении изображения

[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'.

так, очевидно, у меня где-то есть переполнение, но я не могу точно сказать, где.

Если бы кто-нибудь мог мне помочь и объяснить, что я делаю, и указать на все неправильные вещи, которые я сделал, но простым способом, я был бы всегда благодарен.

UPDATE

поэтому, добавив шаг нормализации, мне удалось получить лучшее изображение:

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

И ошибка переполнения исчезла.

Mandelbrot gradient

Но, похоже, что-то не так в преобразовании между HSV и RGB.

Все, что белое, должно быть черным.

Значения HSV, которые я пишу, составляют 255, 255, 0

но он конвертируется в 255, 255, 255 в RGB, что неверно. Он должен быть преобразован в черный.

ОБНОВЛЕНИЕ 2

Как сообщил мне Боб, Hue и Saturation - это не число в диапазоне от 0,1 до 100, а в виде числа от 0 до 0,1.

.

Это была моя главная ошибка, и поэтому я получил переполнение, и черные стали белыми. Исправив это и сделав небольшие исправления, Боб предложил мне теперь получить:

Correct Mandelbrot

1 Ответ

2 голосов
/ 05 июля 2019

В цветовой модели HSV оттенок обычно выражается как угол в градусах [0 °, 360 °), тогда как насыщенность и значение находятся в диапазоне [0, 1].

Способ исправить опубликованный код - настроить значения и их тип, хранящиеся в 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");

Также обратите внимание, что в mandelbrot() вы можете использовать

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