diff --git a/grad.png b/grad.png new file mode 100644 index 0000000..523d4a6 Binary files /dev/null and b/grad.png differ diff --git a/grad2.png b/grad2.png new file mode 100644 index 0000000..eef623b Binary files /dev/null and b/grad2.png differ diff --git a/grad3.jpg b/grad3.jpg new file mode 100644 index 0000000..2c02662 Binary files /dev/null and b/grad3.jpg differ diff --git a/grad3.png b/grad3.png new file mode 100644 index 0000000..691c375 Binary files /dev/null and b/grad3.png differ diff --git a/include/gp_system.h b/include/gp_system.h index c471186..fc28ff9 100644 --- a/include/gp_system.h +++ b/include/gp_system.h @@ -50,4 +50,6 @@ std::tuple&, const std::vector&, const std::vect std::array get_populations(); +void set_use_gamma_correction(bool use); + #endif //GP_SYSTEM_H diff --git a/lib/blt b/lib/blt index 729a16a..d4de353 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 729a16ab574e31bf1b44446a777e4ee834518c6e +Subproject commit d4de35365d2df371b8b61c895ec0f6ae66d46fd8 diff --git a/lib/blt-gp b/lib/blt-gp index 2e09696..b2b584b 160000 --- a/lib/blt-gp +++ b/lib/blt-gp @@ -1 +1 @@ -Subproject commit 2e09696a673eeae099bc87024e6f32e6c5815fa3 +Subproject commit b2b584be519b64b59e6a661322b762d7747b5f96 diff --git a/src/gp_system.cpp b/src/gp_system.cpp index 5641114..19b9c09 100644 --- a/src/gp_system.cpp +++ b/src/gp_system.cpp @@ -38,11 +38,16 @@ float filter_nan(const float f, const float failure = 0.0f) return f; } +bool use_gamma_correction = false; + std::array programs; prog_config_t config{}; std::vector> images; -auto reference_image = image_storage_t::from_file("../silly.png"); +std::vector> images_red; +std::vector> images_green; +std::vector> images_blue; +std::array reference_image; std::vector average_fitness; std::vector best_fitness; @@ -59,23 +64,44 @@ void fitness_func(const tree_t& tree, fitness_t& fitness, const blt::size_t inde { for (blt::size_t y = 0; y < IMAGE_DIMENSIONS; ++y) { - images[index][(x * IMAGE_DIMENSIONS + y) * 3 + Channel] = data.get(x, 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); - auto our = static_cast(data.get(x, y)) / static_cast(std::numeric_limits::max()); + const auto our = static_cast(data.get(x, y)) / static_cast(std::numeric_limits::max()); - auto theirs = reference_image[Channel].get(x, y); + const auto theirs = reference_image[Channel].get(x, y); - auto gamma_ours = std::pow(our, 1.0f / 2.2f); - auto gamma_theirs = std::pow(theirs, 1.0f / 2.2f); + const auto gamma_ours = std::pow(our, 1.0f / 2.2f); + // const auto gamma_theirs = std::pow(theirs, 1.0f / 2.2f); - const auto diff = gamma_ours - gamma_theirs; - fitness.raw_fitness += static_cast((diff * diff)); + if (use_gamma_correction) + { + const auto diff = gamma_ours - theirs; + fitness.raw_fitness += static_cast((diff * diff)); + } else + { + const auto diff = our - theirs; + fitness.raw_fitness += static_cast((diff * diff)); + } } } - // fitness.raw_fitness /= static_cast(IMAGE_SIZE_CHANNELS); - fitness.raw_fitness = static_cast(std::sqrt(fitness.raw_fitness)); - fitness.standardized_fitness = fitness.raw_fitness; - fitness.adjusted_fitness = -fitness.standardized_fitness; + fitness.set_normal(static_cast(std::sqrt(fitness.raw_fitness))); + // fitness.raw_fitness = static_cast(std::sqrt(fitness.raw_fitness)); + // fitness.standardized_fitness = fitness.raw_fitness; + // fitness.adjusted_fitness = -fitness.standardized_fitness; } template @@ -313,14 +339,16 @@ void setup_operations(gp_program* program) operator_builder builder{}; builder.build(op_image_ephemeral, make_add(), make_sub(), make_mul(), make_div(), 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_image_cos_off, op_image_sin_off, op_image_perlin, op_image_noise, op_image_random, op_image_2d_perlin_eph, op_image_not, op_image_grad, - op_image_2d_perlin_oct); + op_image_cos_off, op_image_sin_off, op_image_perlin, op_image_noise, op_image_random, op_image_2d_perlin_eph, op_image_not, + op_image_grad, op_image_2d_perlin_oct); // builder.build(op_image_grad, op_image_x, op_image_y); 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); @@ -340,6 +368,9 @@ void setup_gp_system(const blt::size_t population_size) setup_operations(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{}; @@ -397,6 +428,13 @@ bool should_terminate() std::array& 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]; } @@ -418,6 +456,9 @@ std::array to_gl_image(c { 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); @@ -435,6 +476,12 @@ 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); @@ -467,3 +514,6 @@ std::array 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;} diff --git a/src/image_storage.cpp b/src/image_storage.cpp index 79945ec..b608105 100644 --- a/src/image_storage.cpp +++ b/src/image_storage.cpp @@ -21,13 +21,27 @@ #include #include +inline float srgb_to_linear(const float v) noexcept +{ + return (v <= 0.04045f) ? (v / 12.92f) + : std::pow((v + 0.055f) / 1.055f, 2.4f); +} + + std::array image_storage_t::from_file(const std::string& path) { stbi_set_flip_vertically_on_load(true); int x, y, channels; - auto* data = stbi_loadf(path.c_str(), &x, &y, &channels, 4); + auto* data = stbi_load(path.c_str(), &x, &y, &channels, 4); - const auto resized = stbir_resize_float_linear(data, x, y, 0, nullptr, IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, 0, STBIR_RGBA); + unsigned char* resized = nullptr; + + if (x == IMAGE_DIMENSIONS && y == IMAGE_DIMENSIONS) + { + resized = data; + data = nullptr; + } else + resized = stbir_resize_uint8_srgb(data, x, y, 0, nullptr, IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, 0, STBIR_RGBA); image_storage_t storage_r{}; image_storage_t storage_g{}; @@ -37,9 +51,9 @@ std::array image_storage_t::from_file(const std::string& pat { for (blt::size_t j = 0; j < IMAGE_DIMENSIONS; ++j) { - storage_r.get(i, j) = resized[(j * IMAGE_DIMENSIONS + x) * 4]; - storage_g.get(i, j) = resized[(j * IMAGE_DIMENSIONS + x) * 4 + 1]; - storage_b.get(i, j) = resized[(j * IMAGE_DIMENSIONS + x) * 4 + 2]; + storage_r.get(i, j) = static_cast(resized[(i * IMAGE_DIMENSIONS + j) * 4]) / 255.0f; + storage_g.get(i, j) = static_cast(resized[(i * IMAGE_DIMENSIONS + j) * 4 + 1]) / 255.0f; + storage_b.get(i, j) = static_cast(resized[(i * IMAGE_DIMENSIONS + j) * 4 + 2]) / 255.0f; } } @@ -49,7 +63,9 @@ std::array image_storage_t::from_file(const std::string& pat } std::array image_istorage_t::from_file(const std::string& path) -{} +{ + BLT_ERROR("NOT IMPLEMENTED!"); +} void image_istorage_t::normalize() {} diff --git a/src/main.cpp b/src/main.cpp index 2c1c36b..fe1e648 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,9 @@ void update_population_size(const blt::u32 new_size) for (blt::size_t i = population_size; i < new_size; i++) { auto texture = new texture_gl2D(IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGBA8); + texture->bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl_images.push_back(texture); resources.set(std::to_string(i), texture); } @@ -41,7 +44,6 @@ std::thread run_gp() { return std::thread{ []() { - setup_gp_system(population_size); while (!should_exit) { if (run_generation) @@ -64,10 +66,16 @@ void init(const blt::gfx::window_data&) for (blt::size_t i = 0; i < population_size; i++) { auto texture = new texture_gl2D(IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGBA8); + texture->bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl_images.push_back(texture); resources.set(std::to_string(i), texture); } const auto texture = new texture_gl2D(IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGBA8); + texture->bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); texture->upload(to_gl_image(get_reference_image()).data(), IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGB, GL_FLOAT); resources.set("reference", texture); global_matrices.create_internals(); @@ -98,6 +106,7 @@ void update(const blt::gfx::window_data& data) static blt::i32 image_to_enlarge = -1; bool clicked_on_image = false; + static bool use_gramma_correction = false; static bool show_best = false; // Create the tab bar @@ -128,6 +137,8 @@ void update(const blt::gfx::window_data& data) run_generation = true; ImGui::InputInt("Min Time Between Runs (ms)", &min_between_runs); ImGui::Checkbox("Show Best", &show_best); + if (ImGui::Checkbox("Use Gamma Correction?", &use_gramma_correction)) + set_use_gamma_correction(use_gramma_correction); } ImGui::EndChild(); @@ -218,9 +229,15 @@ void update(const blt::gfx::window_data& data) if (ImGui::BeginTabItem("Reference")) { - blt::f32 w = data.width; - auto h = data.height - top_bar_height - 10; - renderer_2d.drawRectangle({static_cast(w / 2), h / 2, w, h}, "reference"); + auto w = static_cast(data.width); + auto h = static_cast(data.height) - top_bar_height - 10; + + renderer_2d.drawRectangle({ + w / 2, + h / 2, + std::min(w, static_cast(IMAGE_DIMENSIONS)), + std::min(h, static_cast(IMAGE_DIMENSIONS)) + }, "reference"); ImGui::EndTabItem(); } @@ -271,7 +288,8 @@ void update(const blt::gfx::window_data& data) gl_images[i]->upload(get_image(i).data(), IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, GL_RGB, GL_UNSIGNED_INT); } - if ((blt::gfx::isMousePressed(0) && blt::gfx::mousePressedLastFrame() && !clicked_on_image) || (blt::gfx::isKeyPressed(GLFW_KEY_ESCAPE) && blt::gfx::keyPressedLastFrame())) + if ((blt::gfx::isMousePressed(0) && blt::gfx::mousePressedLastFrame() && !clicked_on_image) || (blt::gfx::isKeyPressed(GLFW_KEY_ESCAPE) && + blt::gfx::keyPressedLastFrame())) image_to_enlarge = -1; if (show_best) @@ -281,20 +299,20 @@ void update(const blt::gfx::window_data& data) { const auto width = std::min(static_cast(data.width) - side_bar_width, static_cast(256) * 3) / 3; const auto height = std::min(static_cast(data.height) - top_bar_height, static_cast(256) * 3) / 3; - renderer_2d.drawRectangle(blt::gfx::rectangle2d_t{blt::gfx::anchor_t::BOTTOM_LEFT, - side_bar_width + 256 + i * width, 64, width, height - }, std::to_string(best_image), 1); + renderer_2d.drawRectangle(blt::gfx::rectangle2d_t{blt::gfx::anchor_t::BOTTOM_LEFT, side_bar_width + 256 + i * width, 64, width, height}, + std::to_string(best_image), 1); } } if (image_to_enlarge != -1) { if (blt::gfx::isKeyPressed(GLFW_KEY_R) && blt::gfx::keyPressedLastFrame()) - { - - } - renderer_2d.drawRectangle(blt::gfx::rectangle2d_t{blt::gfx::anchor_t::BOTTOM_LEFT, - side_bar_width + 256, 64, std::min(static_cast(data.width) - side_bar_width, static_cast(256) * 3), + {} + renderer_2d.drawRectangle(blt::gfx::rectangle2d_t{ + blt::gfx::anchor_t::BOTTOM_LEFT, + side_bar_width + 256, + 64, + std::min(static_cast(data.width) - side_bar_width, static_cast(256) * 3), std::min(static_cast(data.height) - top_bar_height, static_cast(256) * 3) }, std::to_string(image_to_enlarge), 1); } @@ -313,6 +331,7 @@ void destroy(const blt::gfx::window_data&) int main() { + setup_gp_system(population_size); auto run_gp_thread = run_gp(); blt::gfx::init(blt::gfx::window_data{"Image GP", init, update, destroy}.setSyncInterval(1)); should_exit = true;