image-gp-2/src/gp_system.cpp

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