#include "methods.hh" #include "common.hh" #include "drawing.hh" #include #include #include #include #include #include #include auto const thread_nbr = std::thread::hardware_concurrency(); std::mutex numbers_mutex; using randint = std::uniform_int_distribution<>; using Color = std::array; using ColorSet = std::vector; using std::rand; namespace methods_private { [[nodiscard]] auto randomColor() { static std::uniform_int_distribution<> dis(0, 255); return cv::Scalar(rand() % 255, rand() % 255, rand() % 255); } [[nodiscard]] auto getColorSet(cv::Mat const& t_reference) { ColorSet res{}; for (int h = 0; h < t_reference.size().height; h += thread_nbr) { std::vector thread_list{}; for (auto i = 0u; i < thread_nbr; ++i) { thread_list.push_back(std::thread(methods_private::threadedGetColor, std::ref(t_reference), std::ref(res), h + i)); } for (auto& th : thread_list) th.join(); } res.shrink_to_fit(); return res; } [[nodiscard]] auto getSquareValues(cv::Mat const& t_img) { int rand_x = rand() % t_img.size().width; int rand_y = rand() % t_img.size().height; int size = rand() % std::min(t_img.size().width - rand_x, t_img.size().height - rand_y); return std::tuple(rand_x, rand_y, size); } [[nodiscard]] auto getControlledSquareValues(cv::Mat const& t_img, int const t_init_iter, int const t_iter) { int rand_x = rand() % t_img.size().width; int rand_y = rand() % t_img.size().height; float const coef = static_cast(t_iter) / static_cast(t_init_iter); int const min_size = static_cast( (static_cast(std::min(t_img.size().width, t_img.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 createCandidate(cv::Mat const& t_base, cv::Mat const& t_ref, ColorSet const& t_colors, double const diff, bool const t_controlled_size = false, int const t_init_iter = 0, int const t_iter = 0) { auto temp_image = t_base.clone(); auto const [rand_x, rand_y, size] = t_controlled_size ? methods_private::getControlledSquareValues( temp_image, t_init_iter, t_iter) : methods_private::getSquareValues(temp_image); methods_private::newSquare2(temp_image, cv::Point{rand_x, rand_y}, size, t_colors[rand() % t_colors.size()]); auto new_diff = euclidian_distance(t_ref, temp_image); return (new_diff < diff) ? std::optional>{std::make_pair( std::move(temp_image), new_diff)} : std::nullopt; } void adjustSize(cv::Mat const& t_process_img, cv::Point& t_top_left, int size) { int const height = t_process_img.size().height; int const width = t_process_img.size().width; 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.y -= diff + 1; } if (int const diff = shape_total_width - width; diff > 0) { t_top_left.x -= diff + 1; } } void threadedGetColor(cv::Mat const& t_reference, ColorSet& t_colors, int t_h) { if (t_h > t_reference.size().height) return; for (int w = 0; w < t_reference.size().width; w += 3) { Color temp = {t_reference.at(t_h, w), t_reference.at(t_h, w + 1), t_reference.at(t_h, w + 2)}; auto pos = std::find(std::begin(t_colors), std::end(t_colors), temp); if (pos == std::end(t_colors)) { numbers_mutex.lock(); t_colors.push_back(temp); numbers_mutex.unlock(); } } } void newSquare1(cv::Mat& t_process_img, cv::Point&& t_top_left, int t_size) { adjustSize(t_process_img, t_top_left, t_size); draw_shape(t_process_img, t_top_left, t_size, randomColor(), Shapes::Square); } void newSquare2(cv::Mat& t_process_img, cv::Point&& t_top_left, int t_size, Color const& t_color) { draw_shape(t_process_img, t_top_left, t_size, cv::Scalar{static_cast(t_color[0]), static_cast(t_color[1]), static_cast(t_color[2])}, Shapes::Square); } } // namespace methods_private void method1(cv::Mat const& t_reference, cv::Mat& t_output, int t_iterations) { auto diff = euclidian_distance(t_reference, t_output); spdlog::debug("Beginning method1, initial difference: {}", diff); while (t_iterations > 0 && diff >= 0) { auto temp_image = t_output.clone(); auto const [rand_x, rand_y, size] = methods_private::getSquareValues(temp_image); methods_private::newSquare1(temp_image, cv::Point{rand_x, rand_y}, size); if (auto const new_diff = euclidian_distance(t_reference, temp_image); new_diff < diff) { diff = new_diff; temp_image.copyTo(t_output); --t_iterations; spdlog::debug("iteration:{} diff:{}", t_iterations, diff); } } } void method2(cv::Mat const& t_reference, cv::Mat& t_output, int t_iterations) { spdlog::debug("Running on {} thread(s).", thread_nbr); auto diff = euclidian_distance(t_reference, t_output); spdlog::debug("Beginning method2, initial difference: {}", diff); auto const colors = methods_private::getColorSet(t_reference); spdlog::debug("{} colors detected.", colors.size()); while (t_iterations > 0) { if (auto result = methods_private::createCandidate(t_output, t_reference, colors, diff); result.has_value()) { diff = result->second; result->first.copyTo(t_output); --t_iterations; spdlog::debug("iteration:{} diff:{}", t_iterations, diff); } } } void method3(cv::Mat const& t_reference, cv::Mat& t_output, int t_iterations) { auto const init_iter = t_iterations; auto diff = euclidian_distance(t_reference, t_output); spdlog::debug("Beginning method2, initial difference: {}", diff); spdlog::debug("Running {} threads.", thread_nbr); auto const colors = methods_private::getColorSet(t_reference); spdlog::debug("{} colors detected.", colors.size()); while (t_iterations > 0) { auto temp_image = t_output.clone(); if (auto result = methods_private::createCandidate( t_output, t_reference, colors, diff, true, init_iter, t_iterations); result.has_value()) { diff = result->second; result->first.copyTo(t_output); --t_iterations; spdlog::debug("iteration:{} diff:{}", t_iterations, diff); } } }