Brett 2024-07-20 15:45:15 -04:00
parent 5352406cbf
commit 42de832ab5
6 changed files with 194 additions and 54 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
project(image-gp-6 VERSION 0.0.13) project(image-gp-6 VERSION 0.0.14)
include(FetchContent) include(FetchContent)

View File

@ -33,7 +33,7 @@ static constexpr blt::size_t DATA_SIZE = IMAGE_SIZE * IMAGE_SIZE;
static constexpr blt::size_t DATA_CHANNELS_SIZE = DATA_SIZE * CHANNELS; static constexpr blt::size_t DATA_CHANNELS_SIZE = DATA_SIZE * CHANNELS;
static constexpr blt::size_t BOX_COUNT = static_cast<blt::size_t>(log2(IMAGE_SIZE / 2)); static constexpr blt::size_t BOX_COUNT = static_cast<blt::size_t>(log2(IMAGE_SIZE / 2));
static constexpr float THRESHOLD = 0.3; static constexpr float THRESHOLD = 0.3;
static constexpr auto load_image = "../029a_-_Survival_of_the_Idiots_349.jpg"; static constexpr auto load_image = "../GSab4SWWcAA1TNR.png";
blt::gp::prog_config_t config = blt::gp::prog_config_t() blt::gp::prog_config_t config = blt::gp::prog_config_t()
.set_initial_min_tree_size(4) .set_initial_min_tree_size(4)
@ -44,6 +44,6 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t()
.set_crossover_chance(1.0) .set_crossover_chance(1.0)
.set_reproduction_chance(0.5) .set_reproduction_chance(0.5)
.set_pop_size(POP_SIZE) .set_pop_size(POP_SIZE)
.set_thread_count(16); .set_thread_count(0);
#endif //IMAGE_GP_6_CONFIG_H #endif //IMAGE_GP_6_CONFIG_H

View File

@ -0,0 +1,22 @@
#pragma once
/*
* Copyright (C) 2024 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/>.
*/
#ifndef IMAGE_GP_6_CUSTOM_TRANSFORMER_H
#define IMAGE_GP_6_CUSTOM_TRANSFORMER_H
#endif //IMAGE_GP_6_CUSTOM_TRANSFORMER_H

@ -1 +1 @@
Subproject commit 5d72923998fd59cf76a7e777642a9c34e76bfb3d Subproject commit f62494d7d896ec3a10266a878e644771ac84aebb

View File

@ -0,0 +1,18 @@
/*
* <Short Description>
* Copyright (C) 2024 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 <custom_transformer.h>

View File

@ -44,6 +44,56 @@ blt::gfx::resource_manager resources;
blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices); blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices);
blt::gfx::first_person_camera_2d camera; blt::gfx::first_person_camera_2d camera;
static constexpr blt::size_t TYPE_COUNT = 1;
std::array<double, POP_SIZE> fitness_values{};
double last_fitness = 0;
double hovered_fitness = 0;
double hovered_fitness_value = 0;
bool evaluate = true;
std::array<bool, TYPE_COUNT> has_literal_converter = {
true
};
std::array<std::function<void(blt::gp::gp_program& program, void*, void*, void*, void*)>, TYPE_COUNT> literal_crossover_funcs = {
[](blt::gp::gp_program&, void* p1_in_ptr, void* p2_in_ptr, void* c1_out_ptr, void* c2_out_ptr) {
auto& p1_in = *static_cast<full_image_t*>(p1_in_ptr);
auto& p2_in = *static_cast<full_image_t*>(p2_in_ptr);
auto& c1_out = *static_cast<full_image_t*>(c1_out_ptr);
auto& c2_out = *static_cast<full_image_t*>(c2_out_ptr);
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
{
auto diff = p1_in.rgb_data[i] - p2_in.rgb_data[i];
c1_out.rgb_data[i] = p1_in.rgb_data[i] - diff;
c2_out.rgb_data[i] = p2_in.rgb_data[i] + diff;
}
}
};
std::array<std::function<void(blt::gp::gp_program& program, void*, void*)>, TYPE_COUNT> literal_mutation_funcs = {
[](blt::gp::gp_program& program, void* p1_in_ptr, void* c1_out_ptr) {
auto& p1_in = *static_cast<full_image_t*>(p1_in_ptr);
auto& c1_out = *static_cast<full_image_t*>(c1_out_ptr);
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
c1_out.rgb_data[i] = p1_in.rgb_data[i] + program.get_random().get_float(-1.0f, 1.0f);
}
};
class image_crossover_t : public blt::gp::crossover_t
{
public:
blt::expected<result_t, error_t> apply(blt::gp::gp_program& program, const blt::gp::tree_t& p1, const blt::gp::tree_t& p2) final
{
auto sel = program.get_random().choice();
if (sel)
return blt::gp::crossover_t::apply(program, p1, p2);
std::abort();
}
};
struct context struct context
{ {
float x, y; float x, y;
@ -126,6 +176,14 @@ blt::gp::operation_t bitwise_or([](const full_image_t& a, const full_image_t& b)
return img; return img;
}, "or"); }, "or");
blt::gp::operation_t bitwise_invert([](const full_image_t& a) {
using blt::mem::type_cast;
full_image_t img{};
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
img.rgb_data[i] = static_cast<float>(~type_cast<unsigned int>(a.rgb_data[i]));
return img;
}, "invert");
blt::gp::operation_t bitwise_xor([](const full_image_t& a, const full_image_t& b) { blt::gp::operation_t bitwise_xor([](const full_image_t& a, const full_image_t& b) {
using blt::mem::type_cast; using blt::mem::type_cast;
full_image_t img{}; full_image_t img{};
@ -201,10 +259,13 @@ blt::gp::operation_t random_val([]() {
i = program.get_random().get_float(0.0f, 1.0f); i = program.get_random().get_float(0.0f, 1.0f);
return img; return img;
}, "color_noise"); }, "color_noise");
static blt::gp::operation_t perlin([](const full_image_t& x, const full_image_t& y, const full_image_t& z) { static blt::gp::operation_t perlin([](const full_image_t& x, const full_image_t& y, const full_image_t& z, const full_image_t& scale) {
full_image_t img{}; full_image_t img{};
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++) for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
img.rgb_data[i] = stb_perlin_noise3(x.rgb_data[i], y.rgb_data[i], z.rgb_data[i], 0, 0, 0); {
auto s = scale.rgb_data[i];
img.rgb_data[i] = stb_perlin_noise3(x.rgb_data[i] / s, y.rgb_data[i] / s, z.rgb_data[i] / s, 0, 0, 0);
}
return img; return img;
}, "perlin"); }, "perlin");
static blt::gp::operation_t perlin_terminal([]() { static blt::gp::operation_t perlin_terminal([]() {
@ -212,10 +273,21 @@ static blt::gp::operation_t perlin_terminal([]() {
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++) for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
{ {
auto ctx = get_ctx(i); auto ctx = get_ctx(i);
img.rgb_data[i] = stb_perlin_noise3(ctx.x / IMAGE_SIZE, ctx.y / IMAGE_SIZE, static_cast<float>(i % CHANNELS) / CHANNELS, 0, 0, 0); img.rgb_data[i] =
stb_perlin_noise3(ctx.x / IMAGE_SIZE, ctx.y / IMAGE_SIZE, static_cast<float>(i % CHANNELS) / CHANNELS, 0, 0, 0);
} }
return img; return img;
}, "perlin_term"); }, "perlin_term");
static blt::gp::operation_t perlin_warped([](const full_image_t& u, const full_image_t& v) {
full_image_t img{};
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
{
auto ctx = get_ctx(i);
img.rgb_data[i] = stb_perlin_noise3(ctx.x / IMAGE_SIZE + u.rgb_data[i], ctx.y / IMAGE_SIZE + v.rgb_data[i],
static_cast<float>(i % CHANNELS) / CHANNELS, 0, 0, 0);
}
return img;
}, "perlin_warped");
static blt::gp::operation_t op_img_size([]() { static blt::gp::operation_t op_img_size([]() {
full_image_t img{}; full_image_t img{};
for (float& i : img.rgb_data) for (float& i : img.rgb_data)
@ -389,8 +461,11 @@ constexpr auto create_fitness_function()
{ {
return [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t index) { return [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t index) {
auto& v = generation_images[index]; auto& v = generation_images[index];
if (evaluate)
v = current_tree.get_evaluation_value<full_image_t>(nullptr); v = current_tree.get_evaluation_value<full_image_t>(nullptr);
if (fitness_values[index] < 0)
{
fitness.raw_fitness = 0; fitness.raw_fitness = 0;
for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++) for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++)
fitness.raw_fitness += compare_values(v.rgb_data[i], base_image.rgb_data[i]); fitness.raw_fitness += compare_values(v.rgb_data[i], base_image.rgb_data[i]);
@ -405,6 +480,9 @@ constexpr auto create_fitness_function()
else else
fitness.raw_fitness += raw.total + raw.combined + 1.0; fitness.raw_fitness += raw.total + raw.combined + 1.0;
fitness.raw_fitness += last_fitness;
} else
fitness.raw_fitness = fitness_values[index];
fitness.standardized_fitness = fitness.raw_fitness; fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
}; };
@ -413,18 +491,29 @@ constexpr auto create_fitness_function()
void execute_generation() void execute_generation()
{ {
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
BLT_TRACE("Evaluate Fitness");
BLT_START_INTERVAL("Image Test", "Fitness");
evaluate = false;
program.evaluate_fitness();
BLT_END_INTERVAL("Image Test", "Fitness");
BLT_START_INTERVAL("Image Test", "Gen"); BLT_START_INTERVAL("Image Test", "Gen");
auto sel = blt::gp::select_tournament_t{}; auto sel = blt::gp::select_tournament_t{};
// auto sel = blt::gp::select_fitness_proportionate_t{};
program.create_next_generation(sel, sel, sel); program.create_next_generation(sel, sel, sel);
BLT_END_INTERVAL("Image Test", "Gen"); BLT_END_INTERVAL("Image Test", "Gen");
BLT_TRACE("Move to next generation"); BLT_TRACE("Move to next generation");
program.next_generation(); program.next_generation();
BLT_TRACE("Evaluate Fitness"); BLT_TRACE("Evaluate Image");
BLT_START_INTERVAL("Image Test", "Fitness"); BLT_START_INTERVAL("Image Test", "Image Eval");
evaluate = true;
program.evaluate_fitness(); program.evaluate_fitness();
BLT_END_INTERVAL("Image Test", "Fitness"); BLT_END_INTERVAL("Image Test", "Image Eval");
BLT_TRACE("----------------------------------------------"); BLT_TRACE("----------------------------------------------");
std::cout << std::endl; std::cout << std::endl;
// reset all fitness values.
for (auto& v : fitness_values)
v = -1;
last_fitness = 0;
} }
void print_stats() void print_stats()
@ -474,8 +563,9 @@ void init(const blt::gfx::window_data&)
type_system.register_type<full_image_t>(); type_system.register_type<full_image_t>();
blt::gp::operator_builder<context> builder{type_system}; blt::gp::operator_builder<context> builder{type_system};
//builder.add_operator(perlin); builder.add_operator(perlin);
builder.add_operator(perlin_terminal); builder.add_operator(perlin_terminal);
builder.add_operator(perlin_warped);
builder.add_operator(add); builder.add_operator(add);
builder.add_operator(sub); builder.add_operator(sub);
@ -490,18 +580,20 @@ void init(const blt::gfx::window_data&)
builder.add_operator(op_v_mod); builder.add_operator(op_v_mod);
builder.add_operator(bitwise_and); builder.add_operator(bitwise_and);
builder.add_operator(bitwise_or); builder.add_operator(bitwise_or);
builder.add_operator(bitwise_invert);
builder.add_operator(bitwise_xor); builder.add_operator(bitwise_xor);
builder.add_operator(dissolve); builder.add_operator(dissolve);
builder.add_operator(hsv_to_rgb); builder.add_operator(hsv_to_rgb);
builder.add_operator(lit, true); builder.add_operator(lit, true);
builder.add_operator(random_val); builder.add_operator(random_val);
builder.add_operator(op_x_r); const bool state = false;
builder.add_operator(op_x_g); builder.add_operator(op_x_r, true);
builder.add_operator(op_x_b); builder.add_operator(op_x_g, true);
builder.add_operator(op_y_r); builder.add_operator(op_x_b, state);
builder.add_operator(op_y_g); builder.add_operator(op_y_r, state);
builder.add_operator(op_y_b); builder.add_operator(op_y_g, state);
builder.add_operator(op_y_b, state);
program.set_operations(builder.build()); program.set_operations(builder.build());
@ -523,7 +615,7 @@ void update(const blt::gfx::window_data& data)
if (ImGui::Begin("Program Control")) if (ImGui::Begin("Program Control"))
{ {
ImGui::Button("Run Generation"); ImGui::Button("Run Generation");
if (ImGui::IsItemClicked() && !is_running) if ((ImGui::IsItemClicked() || (blt::gfx::isKeyPressed(GLFW_KEY_R) && blt::gfx::keyPressedLastFrame())) && !is_running)
{ {
run_generation = true; run_generation = true;
} }
@ -535,6 +627,9 @@ void update(const blt::gfx::window_data& data)
ImGui::Text("Best fitness: %lf", stats.best_fitness.load()); ImGui::Text("Best fitness: %lf", stats.best_fitness.load());
ImGui::Text("Worst fitness: %lf", stats.worst_fitness.load()); ImGui::Text("Worst fitness: %lf", stats.worst_fitness.load());
ImGui::Text("Overall fitness: %lf", stats.overall_fitness.load()); ImGui::Text("Overall fitness: %lf", stats.overall_fitness.load());
ImGui::Separator();
ImGui::Text("Hovered Fitness: %lf", hovered_fitness);
ImGui::Text("Hovered Fitness Value: %lf", hovered_fitness_value);
ImGui::End(); ImGui::End();
} }
@ -557,35 +652,37 @@ void update(const blt::gfx::window_data& data)
renderer_2d.drawRectangleInternal(blt::make_color(0.9, 0.9, 0.3), renderer_2d.drawRectangleInternal(blt::make_color(0.9, 0.9, 0.3),
{x, y, IMAGE_SIZE + IMAGE_PADDING / 2.0f, IMAGE_SIZE + IMAGE_PADDING / 2.0f}, {x, y, IMAGE_SIZE + IMAGE_PADDING / 2.0f, IMAGE_SIZE + IMAGE_PADDING / 2.0f},
10.0f); 10.0f);
auto& ind = program.get_current_pop().get_individuals()[i];
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
if (io.WantCaptureMouse) if (io.WantCaptureMouse)
continue; continue;
hovered_fitness = ind.fitness.adjusted_fitness;
if (blt::gfx::isKeyPressed(GLFW_KEY_LEFT_SHIFT))
{
if (blt::gfx::mousePressedLastFrame()) if (blt::gfx::mousePressedLastFrame())
{ {
auto& ind = program.get_current_pop().get_individuals()[i];
std::cout << "Fitness: " << ind.fitness.adjusted_fitness << " " << ind.fitness.raw_fitness << " "; std::cout << "Fitness: " << ind.fitness.adjusted_fitness << " " << ind.fitness.raw_fitness << " ";
ind.tree.print(program, std::cout, false); ind.tree.print(program, std::cout, false);
std::cout << std::endl; std::cout << std::endl;
} }
} else
// if (blt::gfx::mousePressedLastFrame()) {
// { if (blt::gfx::mousePressedLastFrame() || (blt::gfx::isKeyPressed(GLFW_KEY_F) && blt::gfx::keyPressedLastFrame()))
// if (blt::gfx::isKeyPressed(GLFW_KEY_LEFT_SHIFT)) {
// { fitness_values[i] = last_fitness;
// program_red.get_current_pop().get_individuals()[i].fitness.raw_fitness = static_cast<double>(last_fitness); last_fitness += 1;
// program_green.get_current_pop().get_individuals()[i].fitness.raw_fitness = static_cast<double>(last_fitness); }
// program_blue.get_current_pop().get_individuals()[i].fitness.raw_fitness = static_cast<double>(last_fitness);
// if (blt::gfx::mousePressedLastFrame())
// last_fitness++;
// } else
// selected_image = i;
// }
} }
renderer_2d.drawRectangleInternal(blt::make_vec4(blt::vec3(program.get_current_pop().get_individuals()[i].fitness.adjusted_fitness), 1.0), hovered_fitness_value = fitness_values[i];
}
renderer_2d.drawRectangleInternal(
blt::make_vec4(blt::vec3(static_cast<float>(program.get_current_pop().get_individuals()[i].fitness.adjusted_fitness)), 1.0),
{x, y, IMAGE_SIZE + IMAGE_PADDING / 2.0f, IMAGE_SIZE + IMAGE_PADDING / 2.0f}, {x, y, IMAGE_SIZE + IMAGE_PADDING / 2.0f, IMAGE_SIZE + IMAGE_PADDING / 2.0f},
5.0f); 5.0f);
@ -605,6 +702,9 @@ void update(const blt::gfx::window_data& data)
int main() int main()
{ {
// reset all fitness values.
for (auto& v : fitness_values)
v = -1;
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, 1440, 720}.setSyncInterval(1)); blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, 1440, 720}.setSyncInterval(1));
global_matrices.cleanup(); global_matrices.cleanup();
resources.cleanup(); resources.cleanup();