diff --git a/CMakeLists.txt b/CMakeLists.txt index 127361d..dd2a6aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(image-gp-6 VERSION 0.0.24) +project(image-gp-6 VERSION 0.0.25) include(FetchContent) diff --git a/include/image_operations.h b/include/image_operations.h index 6bd8cca..c3fa721 100644 --- a/include/image_operations.h +++ b/include/image_operations.h @@ -221,6 +221,13 @@ inline blt::gp::operation_t hsv_to_rgb([](const full_image_t& a) { }, "hsv"); inline blt::gp::operation_t lit([]() { + full_image_t img{}; + auto bw = program.get_random().get_float(0.0f, 1.0f); + for (auto& i : img.rgb_data) + i = bw; + return img; +}, "lit"); +inline blt::gp::operation_t vec([]() { full_image_t img{}; auto r = program.get_random().get_float(0.0f, 1.0f); auto g = program.get_random().get_float(0.0f, 1.0f); @@ -232,7 +239,7 @@ inline blt::gp::operation_t lit([]() { img.rgb_data[i * CHANNELS + 2] = b; } return img; -}, "lit"); +}, "vec"); inline blt::gp::operation_t random_val([]() { full_image_t img{}; for (auto& i : img.rgb_data) @@ -262,7 +269,7 @@ inline blt::gp::operation_t perlin_warped([](const full_image_t& u, const full_i for (blt::size_t i = 0; i < DATA_CHANNELS_SIZE; i++) { auto ctx = get_ctx(i); - img.rgb_data[i] = perlin_noise(ctx.x / IMAGE_SIZE + u.rgb_data[i], ctx.y / IMAGE_SIZE + v.rgb_data[i], + img.rgb_data[i] = perlin_noise((ctx.x + + u.rgb_data[i]) / IMAGE_SIZE, (ctx.y + v.rgb_data[i]) / IMAGE_SIZE, static_cast(i % CHANNELS) / CHANNELS); } return img; @@ -397,6 +404,7 @@ void create_image_operations(blt::gp::operator_builder& builder) bool state = false; builder.add_operator(lit, true); + builder.add_operator(vec, true); builder.add_operator(random_val); builder.add_operator(op_x_r, state); builder.add_operator(op_x_g, state); diff --git a/include/images.h b/include/images.h index 73772aa..75649a6 100644 --- a/include/images.h +++ b/include/images.h @@ -24,6 +24,109 @@ #include #include +struct stb_image_t +{ + public: + stb_image_t() = default; + + stb_image_t(const stb_image_t& copy) noexcept: width(copy.width), height(copy.height), channels(copy.channels) + { + data = static_cast(std::malloc(width * height * channels)); + std::memcpy(data, copy.data, width * height * channels); + } + + stb_image_t(stb_image_t&& move) noexcept: + width(move.width), height(move.height), channels(move.channels), data(std::exchange(move.data, nullptr)) + {} + + stb_image_t& operator=(const stb_image_t& copy) + { + if (© == this) + return *this; + if (width != copy.width || height != copy.height || channels != copy.channels) + { + width = copy.width; + height = copy.height; + channels = copy.channels; + stbi_image_free(data); + data = static_cast(std::malloc(width * height * channels * sizeof(float))); + } + std::memcpy(data, copy.data, width * height * channels); + + return *this; + } + + stb_image_t& operator=(stb_image_t&& move) noexcept + { + width = move.width; + height = move.height; + channels = move.channels; + data = std::exchange(move.data, data); + return *this; + } + + stb_image_t& load(const std::string& path) + { + data = stbi_loadf(path.c_str(), &width, &height, &channels, CHANNELS); + channels = CHANNELS; + return *this; + } + + stb_image_t& save(const std::string& str) + { + if (data == nullptr) + return *this; + auto uc_data = std::unique_ptr(new unsigned char[width * height * channels]); + for (int i = 0; i < width * height * channels; i++) + uc_data[i] = static_cast(data[i] * std::numeric_limits::max()); + stbi_write_png(str.c_str(), width, height, channels, uc_data.get(), 0); + return *this; + } + + stb_image_t& resize(int new_width, int new_height) + { + auto new_data = static_cast(malloc(new_width * new_height * channels * sizeof(float))); + stbir_resize_float_linear(data, width, height, 0, new_data, new_width, new_height, 0, + static_cast(CHANNELS)); + stbi_image_free(data); + data = new_data; + width = new_width; + height = new_height; + return *this; + } + + [[nodiscard]] int get_width() const + { + return width; + } + + [[nodiscard]] int get_height() const + { + return height; + } + + float* get_data() + { + return data; + } + + [[nodiscard]] const float* get_data() const + { + return data; + } + + ~stb_image_t() + { + stbi_image_free(data); + data = nullptr; + } + + private: + int width{}, height{}, channels{}; + + float* data = nullptr; +}; + struct full_image_t { float rgb_data[DATA_SIZE * CHANNELS]{}; @@ -44,9 +147,18 @@ struct full_image_t stbi_image_free(data); } - void save(const std::string&) + void load(const stb_image_t& image) { - //stbi_write_png(str.c_str(), IMAGE_SIZE, IMAGE_SIZE, CHANNELS, rgb_data, 0); + stbir_resize_float_linear(image.get_data(), image.get_width(), image.get_height(), 0, rgb_data, IMAGE_SIZE, IMAGE_SIZE, 0, + static_cast(CHANNELS)); + } + + void save(const std::string& str) + { + unsigned char data[DATA_SIZE * CHANNELS]{}; + for (const auto& [index, value] : blt::enumerate(rgb_data)) + data[index] = static_cast(value * std::numeric_limits::max()); + stbi_write_png(str.c_str(), IMAGE_SIZE, IMAGE_SIZE, CHANNELS, data, 0); } friend std::ostream& operator<<(std::ostream& str, const full_image_t&) diff --git a/src/main.cpp b/src/main.cpp index 23cf79f..2aa691f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -122,7 +122,9 @@ class image_crossover_t : public blt::gp::crossover_t std::array generation_images; +int match_method = cv::TM_SQDIFF; full_image_t base_image; +stb_image_t full_base_image; blt::size_t last_run = 0; blt::i32 time_between_runs = 16; bool is_running = false; @@ -246,6 +248,33 @@ constexpr auto create_fitness_function() else fitness.raw_fitness += raw.total + raw.combined + 1.0; + cv::Mat base_image_large{full_base_image.get_width(), full_base_image.get_height(), CV_32FC3, full_base_image.get_data()}; + cv::Mat templ{IMAGE_SIZE, IMAGE_SIZE, CV_32FC3, v.rgb_data}; + cv::Mat result; + + int result_cols = base_image_large.cols - templ.cols + 1; + int result_rows = base_image_large.rows - templ.rows + 1; + + result.create(result_rows, result_cols, CV_32FC1); + + double minVal; + double maxVal; + cv::matchTemplate(base_image_large, templ, result, match_method); + + minMaxLoc(result, &minVal, &maxVal, nullptr, nullptr, cv::Mat()); + + /// For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better + if (match_method == cv::TM_SQDIFF || match_method == cv::TM_SQDIFF_NORMED) + { + if (std::isinf(minVal) || std::isnan(minVal)) + minVal = 200000; + fitness.raw_fitness += minVal * 0.01f; + //BLT_TRACE("%lf, %lf", minVal, maxVal); + } else + { + BLT_WARN("Hello!"); + } + fitness.raw_fitness += last_fitness; } else fitness.raw_fitness = fitness_values[index]; @@ -323,7 +352,9 @@ void init(const blt::gfx::window_data&) BLT_INFO("Using Seed: %ld", SEED); BLT_START_INTERVAL("Image Test", "Main"); BLT_DEBUG("Setup Base Image"); - base_image.load(load_image); + full_base_image.load(load_image).resize(static_cast(std::max(full_base_image.get_width() / 2ul, IMAGE_SIZE)), + static_cast(std::max(full_base_image.get_height() / 2ul, IMAGE_SIZE))); + base_image.load(full_base_image); BLT_DEBUG("Setup Types and Operators"); type_system.register_type(); @@ -457,6 +488,7 @@ int main() BLT_END_INTERVAL("Image Test", "Main"); base_image.save("input.png"); + full_base_image.save("full_input.png"); auto v = get_fractal_value(base_image); BLT_INFO("Base image values per channel: %lf", v.total);