642 lines
24 KiB
C++
642 lines
24 KiB
C++
/*
|
|
* <Short Description>
|
|
* Copyright (C) 2025 Brett Terpstra
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <gp_system.h>
|
|
#include <blt/gp/program.h>
|
|
#include <image_storage.h>
|
|
#include <operations.h>
|
|
#include <random>
|
|
#include "opencv2/imgcodecs.hpp"
|
|
#include "opencv2/imgproc.hpp"
|
|
#include <stb_perlin.h>
|
|
|
|
using namespace blt::gp;
|
|
|
|
bool is_nan(const float f)
|
|
{
|
|
return std::isnan(f) || std::isinf(f) || std::isinf(-f);
|
|
}
|
|
|
|
float filter_nan(const float f, const float failure = 0.0f)
|
|
{
|
|
if (is_nan(f))
|
|
return failure;
|
|
return f;
|
|
}
|
|
|
|
std::vector<float> to_cv2(const image_t& a)
|
|
{
|
|
std::vector<float> converted_data(IMAGE_SIZE);
|
|
for (const auto& [o, v] : blt::in_pairs(converted_data, a.get_data().data))
|
|
o = static_cast<float>(v) / static_cast<float>(std::numeric_limits<blt::u32>::max());
|
|
return converted_data;
|
|
}
|
|
|
|
image_t from_cv2(const std::vector<float>& a)
|
|
{
|
|
image_t ret;
|
|
for (const auto& [o, v] : blt::in_pairs(ret.get_data().data, a))
|
|
o = static_cast<blt::u32>(v * static_cast<float>(std::numeric_limits<blt::u32>::max()));
|
|
return ret;
|
|
}
|
|
|
|
bool use_gamma_correction = false;
|
|
|
|
std::array<gp_program*, 3> programs;
|
|
prog_config_t config{};
|
|
|
|
std::vector<std::array<image_ipixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3>> images;
|
|
std::vector<std::array<image_ipixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS>> images_red;
|
|
std::vector<std::array<image_ipixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS>> images_green;
|
|
std::vector<std::array<image_ipixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS>> images_blue;
|
|
std::array<image_storage_t, 3> reference_image;
|
|
|
|
std::vector<float> average_fitness;
|
|
std::vector<float> best_fitness;
|
|
std::vector<float> worst_fitness;
|
|
std::vector<float> overall_fitness;
|
|
|
|
std::array<std::vector<float>, 3> mean_vec;
|
|
std::array<std::vector<float>, 3> variance_vec;
|
|
|
|
template <size_t Channel>
|
|
void fitness_func(const tree_t& tree, fitness_t& fitness, const blt::size_t index)
|
|
{
|
|
auto image = tree.get_evaluation_ref<image_t>();
|
|
|
|
auto& data = image->get_data();
|
|
for (blt::size_t x = 0; x < IMAGE_DIMENSIONS; ++x)
|
|
{
|
|
for (blt::size_t y = 0; y < IMAGE_DIMENSIONS; ++y)
|
|
{
|
|
switch (Channel)
|
|
{
|
|
case 0:
|
|
images_red[index][(y * IMAGE_DIMENSIONS + x)] = data.get(x, y);
|
|
break;
|
|
case 1:
|
|
images_green[index][(y * IMAGE_DIMENSIONS + x)] = data.get(x, y);
|
|
break;
|
|
case 2:
|
|
images_blue[index][(y * IMAGE_DIMENSIONS + x)] = data.get(x, y);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// images[index][(y * IMAGE_DIMENSIONS + x) * 3 + Channel] = data.get(x, y);
|
|
|
|
const auto our = static_cast<double>(data.get(x, y)) / static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
|
|
const auto theirs = reference_image[Channel].get(x, y);
|
|
|
|
const auto gamma_ours = std::pow(our, 1.0f / 2.2f);
|
|
// const auto gamma_theirs = std::pow(theirs, 1.0f / 2.2f);
|
|
|
|
if (use_gamma_correction)
|
|
{
|
|
const auto diff = gamma_ours - theirs;
|
|
fitness.raw_fitness += static_cast<float>((diff * diff));
|
|
} else
|
|
{
|
|
const auto diff = our - theirs;
|
|
fitness.raw_fitness += static_cast<float>((diff * diff));
|
|
}
|
|
}
|
|
}
|
|
fitness.set_normal(static_cast<float>(std::sqrt(fitness.raw_fitness)));
|
|
// fitness.raw_fitness = static_cast<float>(std::sqrt(fitness.raw_fitness));
|
|
// fitness.standardized_fitness = fitness.raw_fitness;
|
|
// fitness.adjusted_fitness = -fitness.standardized_fitness;
|
|
}
|
|
|
|
template <typename T>
|
|
void setup_operations(gp_program* program)
|
|
{
|
|
static operation_t op_image_x([]() {
|
|
constexpr auto mul = std::numeric_limits<blt::u32>::max() / static_cast<blt::u32>(IMAGE_DIMENSIONS - 1);
|
|
image_t ret{};
|
|
for (blt::u32 x = 0; x < IMAGE_DIMENSIONS; ++x)
|
|
{
|
|
for (blt::u32 y = 0; y < IMAGE_DIMENSIONS; ++y)
|
|
ret.get_data().get(x, y) = y * mul;
|
|
}
|
|
return ret;
|
|
});
|
|
static operation_t op_image_y([]() {
|
|
constexpr auto mul = std::numeric_limits<blt::u32>::max() / static_cast<blt::u32>(IMAGE_DIMENSIONS - 1);
|
|
image_t ret{};
|
|
for (blt::u32 x = 0; x < IMAGE_DIMENSIONS; ++x)
|
|
{
|
|
for (blt::u32 y = 0; y < IMAGE_DIMENSIONS; ++y)
|
|
ret.get_data().get(x, y) = x * mul;
|
|
}
|
|
return ret;
|
|
});
|
|
static auto op_image_random = operation_t([program]() {
|
|
image_t ret{};
|
|
for (auto& v : ret.get_data().data)
|
|
v = program->get_random().get_u32(0, std::numeric_limits<blt::u32>::max());
|
|
return ret;
|
|
});
|
|
static auto op_image_noise = operation_t([program]() {
|
|
image_t ret{};
|
|
for (auto& v : ret.get_data().data)
|
|
v = program->get_random().get_u32(0, std::numeric_limits<blt::u32>::max());
|
|
return ret;
|
|
}).set_ephemeral();
|
|
static auto op_image_ephemeral = operation_t([program]() {
|
|
image_t ret{};
|
|
const auto value = program->get_random().get_u32(0, std::numeric_limits<blt::u32>::max());
|
|
for (auto& v : ret.get_data().data)
|
|
v = value;
|
|
return ret;
|
|
}).set_ephemeral();
|
|
// static operation_t op_image_blend([](const image_t a, const image_t b, const float f) {
|
|
// const auto blend = std::min(std::max(f, 0.0f), 1.0f);
|
|
// const auto beta = 1.0f - blend;
|
|
// image_t ret{};
|
|
// const cv::Mat src1{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, a.as_void_const()};
|
|
// const cv::Mat src2{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, b.as_void_const()};
|
|
// cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, ret.get_data().data.data()};
|
|
// addWeighted(src1, blend, src2, beta, 0.0, dst);
|
|
// return ret;
|
|
// }, "blend_image");
|
|
static operation_t op_image_sin([](const image_t a) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
|
|
ret.get_data().data[i] = static_cast<blt::u32>(((std::sin((v / limit) * blt::PI) + 1.0) / 2.0f) * limit);
|
|
return ret;
|
|
}, "sin_image");
|
|
static operation_t op_image_sin_off([](const image_t a, const image_t b) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, v, off] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = static_cast<blt::u32>(((std::sin((v / limit) * blt::PI * (off / (limit / 4))) + 1.0) / 2.0f) * limit);
|
|
return ret;
|
|
}, "sin_image_off");
|
|
static operation_t op_image_cos([](const image_t a) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
|
|
ret.get_data().data[i] = static_cast<blt::u32>(((std::cos((v / limit) * blt::PI * 2) + 1.0) / 2.0f) * limit);
|
|
return ret;
|
|
}, "cos_image");
|
|
static operation_t op_image_cos_off([](const image_t a, const image_t b) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, v, off] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = static_cast<blt::u32>(((std::cos((v / limit) * blt::PI * (off / (limit / 2))) + 1.0) / 2.0f) * limit);
|
|
return ret;
|
|
}, "cos_image_off");
|
|
static operation_t op_image_log([](const image_t a) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
|
|
{
|
|
if (v == 0)
|
|
ret.get_data().data[i] = 0;
|
|
else
|
|
ret.get_data().data[i] = static_cast<blt::u32>(std::log(v / limit) * limit);
|
|
}
|
|
return ret;
|
|
}, "log_image");
|
|
static operation_t op_image_exp([](const image_t a) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
|
|
ret.get_data().data[i] = static_cast<blt::u32>(std::exp(v / limit) * limit);
|
|
return ret;
|
|
}, "exp_image");
|
|
static operation_t op_image_abs([](const image_t a) {
|
|
image_t ret{};
|
|
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
|
|
ret.get_data().data[i] = std::numeric_limits<blt::u32>::max() - v;
|
|
return ret;
|
|
}, "abs_image");
|
|
static operation_t op_image_mod([](const image_t a, const image_t b) {
|
|
image_t ret{};
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = bv == 0 ? 0 : av % bv;
|
|
return ret;
|
|
}, "mod_image");
|
|
static operation_t op_image_or([](const image_t a, const image_t b) {
|
|
image_t ret{};
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = av | bv;
|
|
return ret;
|
|
}, "bit_or_image");
|
|
static operation_t op_image_and([](const image_t a, const image_t b) {
|
|
image_t ret{};
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = av & bv;
|
|
return ret;
|
|
}, "bit_and_image");
|
|
static operation_t op_image_xor([](const image_t a, const image_t b) {
|
|
image_t ret{};
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = av ^ bv;
|
|
return ret;
|
|
}, "bit_xor_image");
|
|
static operation_t op_image_not([](const image_t a) {
|
|
image_t ret{};
|
|
for (const auto& [i, av] : blt::enumerate(std::as_const(a.get_data().data)).flatten())
|
|
ret.get_data().data[i] = ~av;
|
|
return ret;
|
|
}, "bit_not_image");
|
|
static operation_t op_image_srgb([](const image_t a) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, av] : blt::enumerate(std::as_const(a.get_data().data)).flatten())
|
|
ret.get_data().data[i] = static_cast<blt::u32>(std::pow(av / limit, 1.0/2.2) * limit);
|
|
return ret;
|
|
}, "srgb_image");
|
|
static operation_t op_image_linear([](const image_t a) {
|
|
struct f
|
|
{
|
|
static float srgb_to_linear(const float v) noexcept
|
|
{
|
|
return (v <= 0.04045f) ? (v / 12.92f)
|
|
: std::pow((v + 0.055f) / 1.055f, 2.4f);
|
|
}
|
|
};
|
|
|
|
constexpr auto limit = static_cast<float>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, av] : blt::enumerate(std::as_const(a.get_data().data)).flatten())
|
|
ret.get_data().data[i] = static_cast<blt::u32>(f::srgb_to_linear(static_cast<float>(av) / limit) * limit);
|
|
return ret;
|
|
}, "srgb_image");
|
|
static operation_t op_image_gt([](const image_t a, const image_t b) {
|
|
image_t ret{};
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = av > bv ? av : bv;
|
|
return ret;
|
|
}, "gt_image");
|
|
static operation_t op_image_lt([](const image_t a, const image_t b) {
|
|
image_t ret{};
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
ret.get_data().data[i] = av < bv ? av : bv;
|
|
return ret;
|
|
}, "lt_image");
|
|
static operation_t op_image_grad([](const image_t a, const image_t b) {
|
|
image_t out{};
|
|
|
|
for (const auto& [i, av, bv] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
|
|
{
|
|
const auto p = static_cast<double>(i) / static_cast<double>(IMAGE_SIZE);
|
|
const auto pi = 1 - p;
|
|
|
|
out.get_data().data[i] = static_cast<blt::u32>(av * p + bv * pi);
|
|
}
|
|
return out;
|
|
}, "grad_image");
|
|
static operation_t op_image_perlin([](const image_t a) {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
for (const auto& [i, out, bv] : blt::in_pairs(ret.get_data().data, std::as_const(a.get_data().data)).enumerate().flatten())
|
|
{
|
|
constexpr auto AND = IMAGE_DIMENSIONS - 1;
|
|
const double y = (static_cast<float>(i) / IMAGE_DIMENSIONS) / static_cast<float>(IMAGE_DIMENSIONS);
|
|
const double x = static_cast<float>(i & AND) / static_cast<float>(IMAGE_DIMENSIONS);
|
|
out = static_cast<blt::u32>(stb_perlin_noise3(static_cast<float>(x), static_cast<float>(y), static_cast<float>(bv / (limit * 0.1)), 0, 0,
|
|
0) * limit);
|
|
}
|
|
return ret;
|
|
}, "perlin_image");
|
|
static auto op_image_2d_perlin_eph = operation_t([program]() {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
const auto variety = program->get_random().get_float(1.5, 255);
|
|
const auto x_warp = program->get_random().get_i32(0, 255);
|
|
const auto y_warp = program->get_random().get_i32(0, 255);
|
|
const auto z_warp = program->get_random().get_i32(0, 255);
|
|
|
|
const auto offset_x = program->get_random().get_float(1.0 / 64.0f, 16.0f);
|
|
const auto offset_y = program->get_random().get_float(1.0 / 64.0f, 16.0f);
|
|
|
|
for (const auto& [i, out] : blt::enumerate(ret.get_data().data))
|
|
{
|
|
constexpr auto AND = IMAGE_DIMENSIONS - 1;
|
|
const double y = (static_cast<float>(i) / IMAGE_DIMENSIONS) / static_cast<float>(IMAGE_DIMENSIONS);
|
|
const double x = static_cast<float>(i & AND) / static_cast<float>(IMAGE_DIMENSIONS);
|
|
out = static_cast<blt::u32>(stb_perlin_noise3(static_cast<float>(x) * offset_x, static_cast<float>(y) * offset_y, variety, x_warp, y_warp,
|
|
z_warp) * limit);
|
|
}
|
|
return ret;
|
|
}, "perlin_image_eph").set_ephemeral();
|
|
static auto op_image_2d_perlin_oct = operation_t([program]() {
|
|
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
|
|
image_t ret{};
|
|
const auto rand = program->get_random().get_float(0, 255);
|
|
const auto octaves = program->get_random().get_i32(2, 8);
|
|
const auto gain = program->get_random().get_float(0.1f, 0.9f);
|
|
const auto lac = program->get_random().get_float(1.5f, 6.f);
|
|
|
|
const auto offset = program->get_random().get_float(1.0 / 255.0f, 16.0f);
|
|
|
|
for (const auto& [i, out] : blt::enumerate(ret.get_data().data))
|
|
{
|
|
constexpr auto AND = IMAGE_DIMENSIONS - 1;
|
|
const double y = (static_cast<float>(i) / IMAGE_DIMENSIONS) / static_cast<float>(IMAGE_DIMENSIONS);
|
|
const double x = static_cast<float>(i & AND) / static_cast<float>(IMAGE_DIMENSIONS);
|
|
out = static_cast<blt::u32>(stb_perlin_fbm_noise3(static_cast<float>(x * offset), static_cast<float>(y * offset), rand, lac, gain,
|
|
octaves) * limit);
|
|
}
|
|
return ret;
|
|
}, "perlin_image_eph_oct").set_ephemeral();
|
|
|
|
static operation_t op_passthrough([](const image_t& a) {
|
|
image_t ret{};
|
|
std::memcpy(ret.get_data().data.data(), a.get_data().data.data(), IMAGE_SIZE_BYTES);
|
|
return ret;
|
|
}, "passthrough");
|
|
|
|
static operation_t op_erode([program](const image_t a) {
|
|
const auto erosion_size = program->get_random().get_i32(3, 12);
|
|
std::vector<float> converted_data = to_cv2(a);
|
|
std::vector<float> output_data(IMAGE_SIZE);
|
|
|
|
const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, converted_data.data()};
|
|
cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, output_data.data()};
|
|
|
|
const cv::Mat element = cv::getStructuringElement(cv::MORPH_ERODE, cv::Size(erosion_size, erosion_size));
|
|
cv::erode(src, dst, element);
|
|
|
|
return from_cv2(output_data);
|
|
}, "erode_image");
|
|
|
|
static operation_t op_dilate([program](const image_t a) {
|
|
const auto dilate_size = program->get_random().get_i32(3, 12);
|
|
std::vector<float> converted_data = to_cv2(a);
|
|
std::vector<float> output_data(IMAGE_SIZE);
|
|
|
|
const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, converted_data.data()};
|
|
cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, output_data.data()};
|
|
|
|
const cv::Mat element = cv::getStructuringElement(cv::MORPH_DILATE, cv::Size(dilate_size, dilate_size));
|
|
cv::dilate(src, dst, element);
|
|
|
|
return from_cv2(output_data);
|
|
}, "dilate_image");
|
|
static operation_t op_band_pass([program](const image_t a) {
|
|
auto input = to_cv2(a);
|
|
std::vector<float> output_data(IMAGE_SIZE);
|
|
|
|
const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, input.data()};
|
|
cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, output_data.data()};
|
|
|
|
const auto sigmaLow = program->get_random().get_float(0.5f, 2.0f);
|
|
const auto sigmaHigh = program->get_random().get_float(3.f, 12.f);
|
|
|
|
const auto size = program->get_random().get_i32(1,5) * 2 + 1;
|
|
|
|
cv::Mat low{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F}, high{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F};
|
|
cv::GaussianBlur(src, low, cv::Size(size, size), sigmaLow);
|
|
cv::GaussianBlur(src, high, cv::Size(size, size), sigmaHigh);
|
|
|
|
cv::Mat dog = high - low;
|
|
cv::normalize(dog, dog, 0, 1, cv::NORM_MINMAX);
|
|
dog.copyTo(dst);
|
|
|
|
return from_cv2(output_data);
|
|
}, "band_pass");
|
|
|
|
operator_builder builder{};
|
|
builder.build(op_image_ephemeral, make_add<image_t>(), make_sub<image_t>(), make_mul<image_t>(), make_div<image_t>(), op_image_x, op_image_y,
|
|
op_image_sin, op_image_gt, op_image_lt, op_image_cos, op_image_log, op_image_exp, op_image_or, op_image_and, op_image_xor,
|
|
op_band_pass, op_image_perlin, op_image_noise, op_image_random, op_image_2d_perlin_eph, op_image_not, op_image_srgb,
|
|
op_image_grad, op_image_2d_perlin_oct, op_erode, op_dilate, op_image_abs, op_image_mod, op_image_linear);
|
|
// builder.build(op_thresh, op_image_2d_perlin_oct);
|
|
program->set_operations(builder.grab());
|
|
}
|
|
|
|
void setup_gp_system(const blt::size_t population_size)
|
|
{
|
|
reference_image = image_storage_t::from_file("../silly.png");
|
|
|
|
config.set_pop_size(population_size);
|
|
config.set_elite_count(2);
|
|
config.set_thread_count(0);
|
|
config.set_reproduction_chance(0);
|
|
// config.set_crossover_chance(0);
|
|
// config.set_mutation_chance(0);
|
|
// config.set_reproduction_chance(0);
|
|
|
|
const auto rand = std::random_device()();
|
|
BLT_INFO("Random Seed: {}", rand);
|
|
for (auto& program : programs)
|
|
{
|
|
program = new gp_program{rand, config};
|
|
}
|
|
setup_operations<struct p1>(programs[0]);
|
|
setup_operations<struct p2>(programs[1]);
|
|
setup_operations<struct p3>(programs[2]);
|
|
|
|
images.resize(population_size);
|
|
images_red.resize(population_size);
|
|
images_green.resize(population_size);
|
|
images_blue.resize(population_size);
|
|
|
|
static auto sel = select_tournament_t{};
|
|
|
|
for (const auto program : programs)
|
|
program->generate_initial_population(program->get_typesystem().get_type<image_t>().id());
|
|
|
|
programs[0]->setup_generational_evaluation(fitness_func<0>, sel, sel, sel);
|
|
programs[1]->setup_generational_evaluation(fitness_func<1>, sel, sel, sel);
|
|
programs[2]->setup_generational_evaluation(fitness_func<2>, sel, sel, sel);
|
|
}
|
|
|
|
void run_step()
|
|
{
|
|
BLT_TRACE("------------\\{Begin Generation {}}------------", programs[0]->get_current_generation());
|
|
BLT_TRACE("Creating next generation");
|
|
for (const auto program : programs)
|
|
program->create_next_generation();
|
|
BLT_TRACE("Move to next generation");
|
|
for (const auto program : programs)
|
|
program->next_generation();
|
|
BLT_TRACE("Evaluate Fitness");
|
|
for (const auto program : programs)
|
|
program->evaluate_fitness();
|
|
for (const auto [i, program] : blt::enumerate(programs))
|
|
{
|
|
auto& cur = program->get_current_pop();
|
|
auto mean = std::accumulate(cur.begin(), cur.end(), 0.0, [](const double a, const individual_t& b) {
|
|
return a + b.fitness.adjusted_fitness;
|
|
}) / static_cast<double>(cur.get_individuals().size());
|
|
|
|
auto variance = std::accumulate(cur.begin(), cur.end(), 0.0, [mean](const double a, const individual_t& b) {
|
|
const auto amount = (b.fitness.adjusted_fitness - mean);
|
|
return a + amount * amount;
|
|
}) / static_cast<double>(cur.get_individuals().size());
|
|
|
|
mean_vec[i].push_back(static_cast<float>(mean));
|
|
variance_vec[i].push_back(static_cast<float>(variance));
|
|
|
|
if (i == 0)
|
|
BLT_TRACE("Channel Red");
|
|
else if (i == 1)
|
|
BLT_TRACE("Channel Green");
|
|
else
|
|
BLT_TRACE("Channel Blue");
|
|
BLT_TRACE(" Program has variance of {}", variance);
|
|
|
|
if (program->get_random().choice())
|
|
{
|
|
const auto amount = static_cast<size_t>(static_cast<double>(cur.get_individuals().size()) * 0.1);
|
|
for (size_t j = 0; j < amount; j++)
|
|
{
|
|
static grow_generator_t gen;
|
|
const auto index = cur.get_individuals().size() - 1 - j;
|
|
cur.get_individuals()[index].tree.regen(gen, program->get_typesystem().get_type<image_t>().id(), 6, 10);
|
|
}
|
|
}
|
|
program->evaluate_fitness();
|
|
}
|
|
for (const auto [i, program] : blt::enumerate(programs))
|
|
{
|
|
const auto& stats = program->get_population_stats();
|
|
if (i == 0)
|
|
BLT_TRACE("Channel Red");
|
|
else if (i == 1)
|
|
BLT_TRACE("Channel Green");
|
|
else
|
|
BLT_TRACE("Channel Blue");
|
|
const auto avg = stats.average_fitness.load(std::memory_order_relaxed);
|
|
const auto best = stats.best_fitness.load(std::memory_order_relaxed);
|
|
const auto worst = stats.worst_fitness.load(std::memory_order_relaxed);
|
|
const auto overall = stats.overall_fitness.load(std::memory_order_relaxed);
|
|
|
|
average_fitness.push_back(static_cast<float>(avg));
|
|
best_fitness.push_back(static_cast<float>(best));
|
|
worst_fitness.push_back(static_cast<float>(worst));
|
|
overall_fitness.push_back(static_cast<float>(overall));
|
|
|
|
BLT_TRACE("\tAvg Fit: {:0.6f}, Best Fit: {:0.6f}, Worst Fit: {:0.6f}, Overall Fit: {:0.6f}",
|
|
stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed),
|
|
stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed));
|
|
}
|
|
|
|
BLT_TRACE("----------------------------------------------");
|
|
}
|
|
|
|
bool should_terminate()
|
|
{
|
|
return programs[0]->should_terminate() || programs[1]->should_terminate() || programs[2]->should_terminate();
|
|
}
|
|
|
|
std::array<image_ipixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3>& get_image(const blt::size_t index)
|
|
{
|
|
for (const auto& [i, image_red, image_green, image_blue] : blt::zip(images_red[index], images_green[index], images_blue[index]).enumerate().
|
|
flatten())
|
|
{
|
|
images[index][i * 3] = image_red;
|
|
images[index][i * 3 + 1] = image_green;
|
|
images[index][i * 3 + 2] = image_blue;
|
|
}
|
|
return images[index];
|
|
}
|
|
|
|
void cleanup()
|
|
{
|
|
for (const auto program : programs)
|
|
delete program;
|
|
}
|
|
|
|
const std::array<image_storage_t, 3>& get_reference_image()
|
|
{
|
|
return reference_image;
|
|
}
|
|
|
|
std::array<image_pixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3> to_gl_image(const std::array<image_storage_t, 3>& image)
|
|
{
|
|
std::array<image_pixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3> image_data{};
|
|
for (blt::size_t x = 0; x < IMAGE_DIMENSIONS; ++x)
|
|
{
|
|
for (blt::size_t y = 0; y < IMAGE_DIMENSIONS; ++y)
|
|
{
|
|
// image_data[(x * IMAGE_DIMENSIONS + y) * 3 + 0] = std::pow(image[0].get(x, y), 1.0f / 2.2f);
|
|
// image_data[(x * IMAGE_DIMENSIONS + y) * 3 + 1] = std::pow(image[1].get(x, y), 1.0f / 2.2f);
|
|
// image_data[(x * IMAGE_DIMENSIONS + y) * 3 + 2] = std::pow(image[2].get(x, y), 1.0f / 2.2f);
|
|
image_data[(x * IMAGE_DIMENSIONS + y) * 3 + 0] = image[0].get(x, y);
|
|
image_data[(x * IMAGE_DIMENSIONS + y) * 3 + 1] = image[1].get(x, y);
|
|
image_data[(x * IMAGE_DIMENSIONS + y) * 3 + 2] = image[2].get(x, y);
|
|
}
|
|
}
|
|
return image_data;
|
|
}
|
|
|
|
blt::u32 get_generation()
|
|
{
|
|
return programs[0]->get_current_generation();
|
|
}
|
|
|
|
void set_population_size(const blt::u32 size)
|
|
{
|
|
if (size > images.size())
|
|
images.resize(size);
|
|
if (size > images_red.size())
|
|
images_red.resize(size);
|
|
if (size > images_green.size())
|
|
images_green.resize(size);
|
|
if (size > images_blue.size())
|
|
images_blue.resize(size);
|
|
config.set_pop_size(size);
|
|
for (const auto program : programs)
|
|
program->set_config(config);
|
|
reset_programs();
|
|
}
|
|
|
|
void reset_programs()
|
|
{
|
|
for (const auto program : programs)
|
|
program->reset_program(program->get_typesystem().get_type<image_t>().id());
|
|
}
|
|
|
|
std::array<size_t, 3> get_best_image_index()
|
|
{
|
|
std::array<size_t, 3> best_index{};
|
|
for (const auto [slot, program] : blt::zip(best_index, programs))
|
|
slot = program->get_best_indexes<1>()[0];
|
|
return best_index;
|
|
}
|
|
|
|
void regenerate_image(blt::size_t index, float& image_storage, blt::i32 width, blt::i32 height)
|
|
{}
|
|
|
|
std::tuple<const std::vector<float>&, const std::vector<float>&, const std::vector<float>&, const std::vector<float>&> get_fitness_history()
|
|
{
|
|
return {average_fitness, best_fitness, worst_fitness, overall_fitness};
|
|
}
|
|
|
|
std::array<population_t*, 3> get_populations()
|
|
{
|
|
return {&programs[0]->get_current_pop(), &programs[1]->get_current_pop(), &programs[2]->get_current_pop()};
|
|
}
|
|
|
|
void set_use_gamma_correction(bool use)
|
|
{
|
|
use_gamma_correction = true;
|
|
}
|
|
|
|
std::pair<std::array<std::vector<float>, 3>&, std::array<std::vector<float>, 3>&> get_mean_and_variance()
|
|
{
|
|
return {mean_vec, variance_vec};
|
|
}
|