#include "methods.hh" #include #include #include #include #include #include #include #include std::mutex colors_mutex; auto const thread_nbr = std::thread::hardware_concurrency(); /////////////////////////////////////////////////////////////////////////////// // class implementation // /////////////////////////////////////////////////////////////////////////////// // constructors /////////////////////////////////////////////////////////////// ImageManipulator::ImageManipulator(std::filesystem::path const t_input_path, std::filesystem::path const t_output_path, int const t_iterations) : reference_{cv::imread(t_input_path.native(), cv::IMREAD_COLOR)}, generated_image_{cv::Mat{reference_.size().height, reference_.size().width, CV_8UC3, cv::Scalar(0, 0, 0)}}, output_path_{t_output_path.native()}, diff_{euclidian_distance(generated_image_)}, total_iterations_{t_iterations}, remaining_iter_{t_iterations}, width_{reference_.size().width}, height_{reference_.size().height} { if (!reference_.data) { spdlog::critical("Could not open or find image!\n"); exit(-1); } spdlog::debug("Image loaded!"); spdlog::debug("Width:\t{}", reference_.size().width); spdlog::debug("Height:\t{}", reference_.size().height); } // public methods ///////////////////////////////////////////////////////////// void ImageManipulator::exec_method(int const t_nb_method, bool const t_controlled_size = false) { switch (t_nb_method) { case 1: { method1(); break; } case 2: { method2(); break; } case 3: { method3(); break; } case 4: { method4(t_controlled_size); break; } default: spdlog::error("Requested method {} is not implemented.", t_nb_method); std::exit(-1); } } void ImageManipulator::write_file() const { cv::imwrite(output_path_, generated_image_); } // private methods //////////////////////////////////////////////////////////// [[nodiscard]] auto ImageManipulator::euclidian_distance( cv::Mat const& t_img) const noexcept -> double { double euclidian = 0.0; for (auto itr1 = reference_.begin(), itr2 = t_img.begin(); itr1 != reference_.end() && itr2 != t_img.end(); ++itr1, ++itr2) { euclidian += std::pow(*itr1 - *itr2, 2); } return std::sqrt(euclidian); } [[nodiscard]] auto ImageManipulator::random_color() const noexcept { return cv::Scalar(rand() % 255, rand() % 255, rand() % 255); } [[nodiscard]] auto ImageManipulator::get_square_values() const noexcept { int rand_x = rand() % reference_.size().width; int rand_y = rand() % reference_.size().height; int size = rand() % std::min(reference_.size().width - rand_x, reference_.size().height - rand_y); return std::tuple(rand_x, rand_y, size); } [[nodiscard]] auto ImageManipulator::get_controlled_square_values() const noexcept { int rand_x = rand() % reference_.size().width; int rand_y = rand() % reference_.size().height; float const coef = static_cast(remaining_iter_) / static_cast(total_iterations_); int const min_size = static_cast((static_cast(std::min(reference_.size().width, reference_.size().height)) / 2.0f) * coef); int const max_size = min_size * 2 + 1; int size = rand() % (max_size - min_size) + min_size; return std::tuple(rand_x, rand_y, size); } [[nodiscard]] auto ImageManipulator::create_candidate( bool const t_controlled_size = false) const { auto temp_image = generated_image_.clone(); auto const [rand_x, rand_y, size] = t_controlled_size ? get_controlled_square_values() : get_square_values(); auto const& color = colors_[rand() % colors_.size()]; draw_square( temp_image, cv::Point{rand_x, rand_y}, size, cv::Scalar{static_cast(color[0]), static_cast(color[1]), static_cast(color[2])}); auto new_diff = euclidian_distance(temp_image); return (new_diff < diff_) ? std::optional>{std::make_pair( std::move(temp_image), new_diff)} : std::nullopt; } void ImageManipulator::get_color_set() { for (int h = 0; h < reference_.size().height; h += std::thread::hardware_concurrency()) { std::vector thread_list{}; for (auto i = 0u; i < std::thread::hardware_concurrency(); ++i) { thread_list.push_back( std::thread(&ImageManipulator::threaded_get_color, this, h + i)); } for (auto& th : thread_list) { th.join(); } } colors_.shrink_to_fit(); } void ImageManipulator::threaded_get_color(int t_h) { if (t_h > reference_.size().height) { return; } for (int w = 0; w < reference_.size().width; w += 3) { std::array temp = {reference_.at(t_h, w), reference_.at(t_h, w + 1), reference_.at(t_h, w + 2)}; auto pos = std::find(std::begin(colors_), std::end(colors_), temp); if (pos == std::end(colors_)) { colors_mutex_.lock(); colors_.push_back(std::move(temp)); colors_mutex_.unlock(); } } } void ImageManipulator::adjust_size(cv::Point& t_top_left, int const size) noexcept { int const shape_total_width = t_top_left.x + size; int const shape_total_height = t_top_left.y + size; if (int const diff = shape_total_height + height_; diff > 0) { t_top_left.x += diff + 1; } if (int const diff = shape_total_width + width_; diff > 0) { t_top_left.x += diff + 1; } } void ImageManipulator::draw_square(cv::Mat& t_img, cv::Point const& t_top_left, int const t_size, cv::Scalar const& t_color) const { auto points = std::make_unique(4); points[0] = t_top_left; points[1] = cv::Point{t_top_left.x, t_top_left.y + t_size}; points[2] = cv::Point{t_top_left.x + t_size, t_top_left.y + t_size}; points[3] = cv::Point{t_top_left.x + t_size, t_top_left.y}; fillConvexPoly(t_img, points.get(), 4, t_color); } void ImageManipulator::update_gen_image(cv::Mat const& t_img, double const t_diff) { diff_ = t_diff; t_img.copyTo(generated_image_); --remaining_iter_; spdlog::debug("remaining iter: {}\tdiff: {}", remaining_iter_, diff_); } void ImageManipulator::method1() { spdlog::debug("Beginning method1, initial difference: {}", diff_); spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, remaining_iter_); while (remaining_iter_ > 0 && diff_ > 0.0) { auto temp_image = generated_image_.clone(); auto const [rand_x, rand_y, size] = get_square_values(); draw_square(temp_image, cv::Point{rand_x, rand_y}, size, random_color()); if (auto const new_diff = euclidian_distance(temp_image); new_diff < diff_) { update_gen_image(temp_image, new_diff); } } } void ImageManipulator::method2() { spdlog::debug("Beginning method2, initial difference: {}", diff_); spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, remaining_iter_); spdlog::debug("Running on {} threads", std::thread::hardware_concurrency()); get_color_set(); spdlog::debug("{} colors detected", colors_.size()); while (remaining_iter_ > 0 && diff_ > 0.0) { if (auto result = create_candidate(); result.has_value()) { update_gen_image(result->first, result->second); } } } void ImageManipulator::method3() { spdlog::debug("Beginning method2, initial difference: {}", diff_); spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, remaining_iter_); spdlog::debug("Running on {} threads", std::thread::hardware_concurrency()); get_color_set(); spdlog::debug("{} colors detected", colors_.size()); while (remaining_iter_ > 0 && diff_ > 0.0) { auto temp_image = generated_image_.clone(); if (auto result = create_candidate(true); result.has_value()) { update_gen_image(result->first, result->second); } } } void ImageManipulator::method4(bool const t_controlled_size) { spdlog::debug("Beginning method2, initial difference: {}", diff_); spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, remaining_iter_); spdlog::debug("Running on {} threads", std::thread::hardware_concurrency()); get_color_set(); spdlog::debug("{} colors detected", colors_.size()); while(remaining_iter_ > 0 && diff_ > 0.0) { std::vector>>> results{}; std::vector> values{}; for (size_t i = 0; i < std::thread::hardware_concurrency(); ++i) { results.push_back(std::async(std::launch::async, &ImageManipulator::create_candidate, this, t_controlled_size)); } for (auto& elem : results) { if(auto res = elem.get(); res.has_value() && res->second < diff_) { values.push_back(*res); } } if(values.size() > 0) { size_t best = 0; for(size_t i = 0; i < values.size(); ++i) { if(values[i].second < values[best].second) { best = i; } } update_gen_image(values[best].first, values[best].second); } } }