diff --git a/include/genimg/methods.hh b/include/genimg/methods.hh index 70815c9..ef73521 100644 --- a/include/genimg/methods.hh +++ b/include/genimg/methods.hh @@ -1,150 +1,142 @@ #pragma once #include "shapes.hh" -#include + #include #include #include -class ImageManipulator { - public: - ImageManipulator() = delete; +class ImageManipulator +{ +public: + ImageManipulator() = delete; - /// \brief Copy contructor - ImageManipulator(const ImageManipulator& other); + /// \brief Copy contructor + ImageManipulator(const ImageManipulator &other); - /// \brief Move constructor - ImageManipulator(ImageManipulator&& other) noexcept; + /// \brief Move constructor + ImageManipulator(ImageManipulator &&other) noexcept; - /// \brief Load image from input, and prepare for output - ImageManipulator(std::string const t_input_path, - std::string const t_output_path, - int const iterations, - Shape::ShapeType const t_shape); + /// \brief Load image from input, and prepare for output + ImageManipulator(std::string const t_input_path, + std::string const t_output_path, int const iterations, + Shape::ShapeType const t_shape); - /// \brief Basically makes views from image - ImageManipulator(cv::Mat const& t_origin_image, - int const t_iterations, - Shape::ShapeType const t_shape, - int const t_x, - int const t_y, - int const t_width, - int const t_height); + /// \brief Basically makes views from image + ImageManipulator(cv::Mat const &t_origin_image, int const t_iterations, + Shape::ShapeType const t_shape, int const t_x, int const t_y, + int const t_width, int const t_height); - [[nodiscard]] auto operator=(ImageManipulator& other) = delete; + [[nodiscard]] auto operator=(ImageManipulator &other) = delete; - [[nodiscard]] auto operator=(ImageManipulator&& other) noexcept = delete; + [[nodiscard]] auto operator=(ImageManipulator &&other) noexcept = delete; - /// \brief Execute the nth method on the current object - void exec_method(int const t_nb_method, - bool const t_controlled_size, - int const t_cols, - int const t_rows, - int const t_submethod); + /// \brief Execute the nth method on the current object + void exec_method(int const t_nb_method, bool const t_controlled_size, + int const t_cols, int const t_rows, int const t_submethod); - /** - * \brief Write the generated image to the output path - * - * Write the generated image as a file to the specified path stored in the - * object - */ - inline void write_file() const - { - cv::imwrite(output_path_, generated_image_); - } + /** + * \brief Write the generated image to the output path + * + * Write the generated image as a file to the specified path stored in the + * object + */ + inline void write_file() const + { + cv::imwrite(output_path_, generated_image_); + } - /// \brief Returns a reference to the generated image - [[nodiscard]] inline auto const& get_generated_image() const noexcept - { - return generated_image_; - } + /// \brief Returns a reference to the generated image + [[nodiscard]] inline auto const &get_generated_image() const noexcept + { + return generated_image_; + } - /// \brief Destructor - virtual ~ImageManipulator() noexcept = default; + /// \brief Destructor + virtual ~ImageManipulator() noexcept = default; - protected: - private: - // methods ////////////////////////////////////////////////////////////////// +protected: +private: + // methods ////////////////////////////////////////////////////////////////// - /// \brief Calculates the euclidian distance between two images - [[nodiscard]] auto euclidian_distance(cv::Mat const& t_img) const noexcept - -> double; + /// \brief Calculates the euclidian distance between two images + [[nodiscard]] auto euclidian_distance(cv::Mat const &t_img) const noexcept + -> double; - /// \brief Creates and returns a random color - [[nodiscard]] auto random_color() const noexcept; + /// \brief Creates and returns a random color + [[nodiscard]] auto random_color() const noexcept; - /// \brief Generates random square coordinates - [[deprecated]] [[nodiscard]] auto get_square_values() const noexcept; + /// \brief Generates random square coordinates + [[deprecated]] [[nodiscard]] auto get_square_values() const noexcept; - /// \brief Generates controlled random square coordinates - [[deprecated]] [[nodiscard]] auto get_controlled_square_values() const - noexcept; + /// \brief Generates controlled random square coordinates + [[deprecated]] [[nodiscard]] auto get_controlled_square_values() const + noexcept; - /// \brief Generates a candidate for image generation improvement - [[nodiscard]] auto create_candidate(bool const t_controlled_size); + /// \brief Generates a candidate for image generation improvement + [[nodiscard]] auto create_candidate(bool const t_controlled_size); - /// \brief Generates organized views of the reference image for method 5 - [[nodiscard]] auto generate_tiles(int const t_cols, int const t_rows) const; + /// \brief Generates organized views of the reference image for method 5 + [[nodiscard]] auto generate_tiles(int const t_cols, int const t_rows) const; - /// \brief Gets all colors from the reference image - void get_color_set(); + /// \brief Gets all colors from the reference image + void get_color_set(); - /// \brief Threaded helper for \ref get_color_set - void threaded_get_color(int const t_h); + /// \brief Threaded helper for \ref get_color_set + void threaded_get_color(int const t_h); - /// \brief Draw a square on an image - [[deprecated]] void draw_square(cv::Mat& t_img, - cv::Point const& t_top_left, - int const t_size, - cv::Scalar const& t_color) const; + /// \brief Draw a square on an image + [[deprecated]] void draw_square(cv::Mat &t_img, cv::Point const &t_top_left, + int const t_size, + cv::Scalar const &t_color) const; - void draw_shape(cv::Mat& t_img, cv::Scalar&& t_color); + void draw_shape(cv::Mat &t_img, cv::Scalar &&t_color); + void create_shape() noexcept; + void create_controlled_shape() noexcept; - /// \brief Update this object’s generated image - void update_gen_image(cv::Mat const& t_img, double const t_diff); + /// \brief Update this object’s generated image + void update_gen_image(cv::Mat const &t_img, double const t_diff); - /// \brief Merges tiles generated by method5 - void merge_tiles(std::vector> const& t_tiles); + /// \brief Merges tiles generated by method5 + void merge_tiles(std::vector> const &t_tiles); - /// \brief First method as described in the - /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) - void method1(); + /// \brief First method as described in the + /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) + void method1(); - /// \brief Second method as described in the - /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) - void method2(); + /// \brief Second method as described in the + /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) + void method2(); - /// \brief Third method as described in the - /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) - void method3(); + /// \brief Third method as described in the + /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) + void method3(); - /// \brief Fourth method as described in the - /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) - void method4(bool const t_controlled_size); + /// \brief Fourth method as described in the + /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) + void method4(bool const t_controlled_size); - /// \brief Fifth method as described in the - /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) - void method5(bool const t_controlled_size, - int const cols, - int const rows, - int const submethod); + /// \brief Fifth method as described in the + /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) + void method5(bool const t_controlled_size, int const cols, int const rows, + int const submethod); - // members ////////////////////////////////////////////////////////////////// + // members ////////////////////////////////////////////////////////////////// - std::vector> colors_{}; /*!< Color set from reference */ - cv::Mat const reference_{}; /*!< Reference image */ - cv::Mat generated_image_{ - reference_.size().height, reference_.size().width, CV_8UC3, - cv::Scalar(0, 0, 0)}; /*!< Working, generated image */ - Shape shape_{Shape::ShapeType::Square}; - mutable std::mutex - colors_mutex_{}; /*!< Thread mutex for color set generation */ - std::string const output_path_{}; /*!< Write path for the generated image */ - double diff_{euclidian_distance(generated_image_)}; /*!< Euclidian difference - between \ref reference_ and \ref generated_image_ */ - int const total_iterations_{0}; /*!< Number of iterations to perform */ - int remaining_iter_{ - total_iterations_}; /*!< Remaining iterations to perform */ - int const width_{reference_.size().width}; /*!< Width of the image */ - int const height_{reference_.size().height}; /*!< Height of the image */ + std::vector> colors_{}; /*!< Color set from reference */ + cv::Mat const reference_{}; /*!< Reference image */ + cv::Mat generated_image_{ + reference_.size().height, reference_.size().width, CV_8UC3, + cv::Scalar(0, 0, 0)}; /*!< Working, generated image */ + Shape shape_{Shape::ShapeType::Square}; + mutable std::mutex + colors_mutex_{}; /*!< Thread mutex for color set generation */ + std::string const output_path_{}; /*!< Write path for the generated image */ + double diff_{euclidian_distance(generated_image_)}; /*!< Euclidian difference + between \ref reference_ and \ref generated_image_ */ + int const total_iterations_{0}; /*!< Number of iterations to perform */ + int remaining_iter_{ + total_iterations_}; /*!< Remaining iterations to perform */ + int const width_{reference_.size().width}; /*!< Width of the image */ + int const height_{reference_.size().height}; /*!< Height of the image */ }; diff --git a/include/genimg/parseargs.hh b/include/genimg/parseargs.hh index ecd40d4..9eff580 100644 --- a/include/genimg/parseargs.hh +++ b/include/genimg/parseargs.hh @@ -1,20 +1,21 @@ #pragma once -#include #include "shapes.hh" +#include + struct ParsedArgs { - std::filesystem::path input_path; - std::filesystem::path output_path; + std::filesystem::path input_path; + std::filesystem::path output_path; Shape::ShapeType shape; - int iterations; - int method; - int cols; - int rows; - int submethod; - bool controlled_size; - bool verbose; + int iterations; + int method; + int cols; + int rows; + int submethod; + bool controlled_size; + bool verbose; }; /// \brief Parses the arguments passed to the program -[[nodiscard]] auto parse_args(int, char**) -> ParsedArgs; +[[nodiscard]] auto parse_args(int, char **) -> ParsedArgs; diff --git a/include/genimg/shapes.hh b/include/genimg/shapes.hh index abc9f5b..ce526b3 100644 --- a/include/genimg/shapes.hh +++ b/include/genimg/shapes.hh @@ -4,60 +4,62 @@ #include #include -class Shape { - public: - static constexpr int MAX_POINTS{4}; - enum class ShapeType { Square, Triangle }; +class Shape +{ +public: + static constexpr int MAX_POINTS{4}; + enum class ShapeType { Square, Triangle }; - /// \brief Default constructor - Shape() = delete; + /// \brief Default constructor + Shape() = delete; - Shape(Shape::ShapeType const t_type); + Shape(Shape::ShapeType const t_type); - /// \brief Copy constructor - Shape(const Shape& other) = default; + /// \brief Copy constructor + Shape(const Shape &other) = default; - /// \brief Move constructor - Shape(Shape&& other) noexcept; + /// \brief Move constructor + Shape(Shape &&other) noexcept; - /// \brief Destructor - virtual ~Shape() noexcept = default; + /// \brief Destructor + virtual ~Shape() noexcept = default; - /// \brief Copy assignment operator - Shape& operator=(const Shape& other) = delete; + /// \brief Copy assignment operator + Shape &operator=(const Shape &other) = delete; - /// \brief Move assignment operator - Shape& operator=(Shape&& other) noexcept = delete; + /// \brief Move assignment operator + Shape &operator=(Shape &&other) noexcept = delete; - /// \brief Generates a shape's points - void operator()(cv::Point&& t_max_pos, - int const t_max_size, - int const t_min_size = 0) noexcept; + /// \brief Generates a shape's points + void operator()(cv::Point &&t_max_pos, int const t_max_size, + int const t_min_size = 0) noexcept; - [[nodiscard]] auto get_points() const noexcept - -> std::array const& - { - return points_; - } + [[nodiscard]] auto get_points() const noexcept + -> std::array const & + { + return points_; + } - /// \brief Returns the type of shape described by the object - [[nodiscard]] auto get_type() const noexcept -> ShapeType const& - { - return type_; - } + /// \brief Returns the type of shape described by the object + [[nodiscard]] auto get_type() const noexcept -> ShapeType const & + { + return type_; + } - [[nodiscard]] auto get_nb_points() const noexcept { return nb_points_; } + [[nodiscard]] auto get_nb_points() const noexcept + { + return nb_points_; + } - protected: - private: - void create_square_points(cv::Point const& t_top_left, - int const t_size) noexcept; - void create_triangle_points(cv::Point const& t_top_left, - int const t_size) noexcept; +protected: +private: + void create_square_points(cv::Point const &t_top_left, + int const t_size) noexcept; + void create_triangle_points(cv::Point const &t_top_left, + int const t_size) noexcept; - - ShapeType const type_{ShapeType::Square}; - std::array points_{ - cv::Point{0, 0}, cv::Point{0, 0}, cv::Point{0, 0}, cv::Point{0, 0}}; - int nb_points_{Shape::MAX_POINTS}; + ShapeType const type_{ShapeType::Square}; + std::array points_{ + cv::Point{0, 0}, cv::Point{0, 0}, cv::Point{0, 0}, cv::Point{0, 0}}; + int nb_points_{Shape::MAX_POINTS}; }; diff --git a/src/main.cc b/src/main.cc index ba0fcaf..4f26ca8 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,24 +1,25 @@ #include "methods.hh" #include "parseargs.hh" + #include #include #include -int main(int ac, char** av) +int main(int ac, char **av) { - std::srand(std::time(nullptr)); - auto const arguments = parse_args(ac, av); - spdlog::set_level(arguments.verbose ? spdlog::level::debug - : spdlog::level::info); - spdlog::set_pattern("[thread %t] %+"); - spdlog::debug("Input file:\t{}", arguments.input_path.native()); - spdlog::debug("Output file:\t{}", arguments.output_path.native()); - spdlog::debug("Iterations:\t{}", arguments.iterations); + std::srand(std::time(nullptr)); + auto const arguments = parse_args(ac, av); + spdlog::set_level(arguments.verbose ? spdlog::level::debug + : spdlog::level::info); + spdlog::set_pattern("[thread %t] %+"); + spdlog::debug("Input file:\t{}", arguments.input_path.native()); + spdlog::debug("Output file:\t{}", arguments.output_path.native()); + spdlog::debug("Iterations:\t{}", arguments.iterations); - ImageManipulator image_process{arguments.input_path, arguments.output_path, - arguments.iterations, arguments.shape}; - image_process.exec_method(arguments.method, arguments.controlled_size, - arguments.cols, arguments.rows, - arguments.submethod); - image_process.write_file(); + ImageManipulator image_process{arguments.input_path, arguments.output_path, + arguments.iterations, arguments.shape}; + image_process.exec_method(arguments.method, arguments.controlled_size, + arguments.cols, arguments.rows, + arguments.submethod); + image_process.write_file(); } diff --git a/src/methods.cc b/src/methods.cc index 02bf8bd..e292752 100644 --- a/src/methods.cc +++ b/src/methods.cc @@ -1,4 +1,5 @@ #include "methods.hh" + #include #include #include @@ -17,16 +18,12 @@ static auto const thread_nbr = std::thread::hardware_concurrency(); * * \param[in] other Element to copy */ -ImageManipulator::ImageManipulator(const ImageManipulator& other) - : colors_{other.colors_}, - reference_{other.reference_}, - generated_image_{other.generated_image_}, - shape_{other.shape_}, - output_path_{other.output_path_}, - diff_{other.diff_}, +ImageManipulator::ImageManipulator(const ImageManipulator &other) + : colors_{other.colors_}, reference_{other.reference_}, + generated_image_{other.generated_image_}, shape_{other.shape_}, + output_path_{other.output_path_}, diff_{other.diff_}, total_iterations_{other.total_iterations_}, - remaining_iter_{other.remaining_iter_}, - width_{other.width_}, + remaining_iter_{other.remaining_iter_}, width_{other.width_}, height_{other.height_} { } @@ -37,16 +34,14 @@ ImageManipulator::ImageManipulator(const ImageManipulator& other) * * \param[in] other Element to move */ -ImageManipulator::ImageManipulator(ImageManipulator&& other) noexcept - : colors_{std::move(other.colors_)}, - reference_{std::move(other.reference_)}, - generated_image_{std::move(other.generated_image_)}, - shape_{std::move(other.shape_)}, +ImageManipulator::ImageManipulator(ImageManipulator &&other) noexcept + : colors_{std::move(other.colors_)}, reference_{std::move( + other.reference_)}, + generated_image_{std::move(other.generated_image_)}, shape_{std::move( + other.shape_)}, output_path_{std::move(other.output_path_)}, - diff_{std::move(other.diff_)}, - total_iterations_{other.total_iterations_}, - remaining_iter_{other.remaining_iter_}, - width_{other.width_}, + diff_{std::move(other.diff_)}, total_iterations_{other.total_iterations_}, + remaining_iter_{other.remaining_iter_}, width_{other.width_}, height_{other.height_} { } @@ -63,15 +58,14 @@ ImageManipulator::ImageManipulator(std::string const t_input_path, std::string const t_output_path, int const t_iterations, Shape::ShapeType const t_shape) - : reference_{cv::imread(t_input_path, cv::IMREAD_COLOR)}, - shape_{Shape{t_shape}}, - output_path_{t_output_path}, - total_iterations_{t_iterations} + : reference_{cv::imread(t_input_path, cv::IMREAD_COLOR)}, shape_{Shape{ + t_shape}}, + output_path_{t_output_path}, total_iterations_{t_iterations} { - if (!reference_.data) { - spdlog::critical("Could not open or find image!\n"); - exit(-1); - } + if (!reference_.data) { + spdlog::critical("Could not open or find image!\n"); + exit(-1); + } } /** @@ -85,23 +79,20 @@ ImageManipulator::ImageManipulator(std::string const t_input_path, * \param[in] t_width Width of the view from its origin * \param[in] t_height Height of the view from its origin */ -ImageManipulator::ImageManipulator(cv::Mat const& t_origin_image, +ImageManipulator::ImageManipulator(cv::Mat const &t_origin_image, int const t_iterations, Shape::ShapeType const t_shape, - int const t_x, - int const t_y, - int const t_width, - int const t_height) + int const t_x, int const t_y, + int const t_width, int const t_height) : reference_{t_origin_image( cv::Range{t_y, std::min(t_y + t_height, t_origin_image.rows)}, cv::Range{t_x, std::min(t_x + t_width, t_origin_image.cols)})}, - shape_{Shape{t_shape}}, - total_iterations_{t_iterations} + shape_{Shape{t_shape}}, total_iterations_{t_iterations} { - if (!reference_.data) { - spdlog::critical("Could not open or find image!\n"); - exit(-1); - } + if (!reference_.data) { + spdlog::critical("Could not open or find image!\n"); + exit(-1); + } } // public methods ///////////////////////////////////////////////////////////// @@ -121,35 +112,19 @@ ImageManipulator::ImageManipulator(cv::Mat const& t_origin_image, */ void ImageManipulator::exec_method(int const t_nb_method, bool const t_controlled_size = false, - int const t_cols = 1, - int const t_rows = 0, + int const t_cols = 1, int const t_rows = 0, int const t_submethod = 1) { - switch (t_nb_method) { - case 1: { - method1(); - break; - } - case 2: { - method2(); - break; - } - case 3: { - method3(); - break; - } - case 4: { - method4(t_controlled_size); - break; - } - case 5: { - method5(t_controlled_size, t_cols, t_rows, t_submethod); - break; - } - default: - spdlog::error("Requested method {} is not implemented.", t_nb_method); - std::exit(-1); - } + switch (t_nb_method) { + case 1: method1(); break; + case 2: method2(); break; + case 3: method3(); break; + case 4: method4(t_controlled_size); break; + case 5: method5(t_controlled_size, t_cols, t_rows, t_submethod); break; + default: + spdlog::error("Requested method {} is not implemented.", t_nb_method); + std::exit(-1); + } } /////////////////////////////////////////////////////////////////////////////// @@ -165,16 +140,17 @@ void ImageManipulator::exec_method(int const t_nb_method, * \param t_img Image with which the distance is computed * \return double */ -[[nodiscard]] auto ImageManipulator::euclidian_distance( - cv::Mat const& t_img) const noexcept -> double +[[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); + 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); } /** @@ -182,7 +158,7 @@ void ImageManipulator::exec_method(int const t_nb_method, */ [[nodiscard]] auto ImageManipulator::random_color() const noexcept { - return cv::Scalar(rand() % 255, rand() % 255, rand() % 255); + return cv::Scalar(rand() % 255, rand() % 255, rand() % 255); } /** @@ -192,12 +168,12 @@ void ImageManipulator::exec_method(int const t_nb_method, */ [[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); + 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); } /** @@ -210,18 +186,38 @@ void ImageManipulator::exec_method(int const t_nb_method, [[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); + 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); +} + +void ImageManipulator::create_shape() noexcept +{ + shape_(cv::Point{reference_.size().width, reference_.size().height}, + std::min(reference_.size().width, reference_.size().height)); +} + +void ImageManipulator::create_controlled_shape() noexcept +{ + 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; + shape_(cv::Point{reference_.size().width, reference_.size().height}, max_size, + min_size); } /** @@ -233,41 +229,24 @@ void ImageManipulator::exec_method(int const t_nb_method, * \param[in] t_controlled_size Enables controlled square size * \return Optional pair of cv::Mat and double */ -[[nodiscard]] auto ImageManipulator::create_candidate( - bool const t_controlled_size = false) +[[nodiscard]] auto +ImageManipulator::create_candidate(bool const t_controlled_size = false) { - auto temp_img = generated_image_.clone(); - auto create_shape = [&]() { - return shape_(cv::Point{reference_.size().width, reference_.size().height}, - std::min(reference_.size().width, reference_.size().height)); - }; - auto create_controlled_shape = [&]() { - 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; - return shape_(cv::Point{reference_.size().width, reference_.size().height}, - max_size, min_size); - }; - auto const& color = colors_[rand() % colors_.size()]; - if (t_controlled_size) { - create_shape(); - } - else { - create_controlled_shape(); - } - draw_shape(temp_img, cv::Scalar{static_cast(color[0]), - static_cast(color[1]), - static_cast(color[2])}); - auto new_diff = euclidian_distance(temp_img); - return (new_diff < diff_) - ? std::optional>{std::make_pair( - std::move(temp_img), new_diff)} - : std::nullopt; + auto temp_img = generated_image_.clone(); + auto const &color = colors_[rand() % colors_.size()]; + if (t_controlled_size) { + create_shape(); + } else { + create_controlled_shape(); + } + draw_shape(temp_img, cv::Scalar{static_cast(color[0]), + static_cast(color[1]), + static_cast(color[2])}); + auto new_diff = euclidian_distance(temp_img); + return (new_diff < diff_) + ? std::optional>{std::make_pair( + std::move(temp_img), new_diff)} + : std::nullopt; } /** @@ -281,25 +260,25 @@ void ImageManipulator::exec_method(int const t_nb_method, [[nodiscard]] auto ImageManipulator::generate_tiles(int const t_cols, int const t_rows) const { - std::vector> tiles{}; - int const tile_width = reference_.cols / t_cols; - int const tile_height = reference_.rows / t_rows; - for (int index_x = 0; index_x < t_cols; ++index_x) { - std::vector tile_col{}; - for (int index_y = 0; index_y < t_rows; ++index_y) { - int const width = (index_x != t_cols - 1) - ? tile_width - : tile_width + reference_.cols % tile_width; - int const height = (index_y != t_rows - 1) - ? tile_height - : tile_height + reference_.rows % tile_height; - tile_col.emplace_back(reference_, total_iterations_, shape_.get_type(), - index_x * tile_width, index_y * tile_height, width, - height); - } - tiles.push_back(tile_col); - } - return tiles; + std::vector> tiles{}; + int const tile_width = reference_.cols / t_cols; + int const tile_height = reference_.rows / t_rows; + for (int index_x = 0; index_x < t_cols; ++index_x) { + std::vector tile_col{}; + for (int index_y = 0; index_y < t_rows; ++index_y) { + int const width = (index_x != t_cols - 1) + ? tile_width + : tile_width + reference_.cols % tile_width; + int const height = (index_y != t_rows - 1) + ? tile_height + : tile_height + reference_.rows % tile_height; + tile_col.emplace_back(reference_, total_iterations_, shape_.get_type(), + index_x * tile_width, index_y * tile_height, width, + height); + } + tiles.push_back(tile_col); + } + return tiles; } /** @@ -308,16 +287,16 @@ void ImageManipulator::exec_method(int const t_nb_method, */ void ImageManipulator::get_color_set() { - for (int h = 0; h < reference_.size().height; h += thread_nbr) { - std::vector thread_list{}; - for (auto i = 0u; i < thread_nbr; ++i) { - thread_list.push_back( - std::thread(&ImageManipulator::threaded_get_color, this, h + i)); - } - std::for_each(thread_list.begin(), thread_list.end(), - [](auto& th) { th.join(); }); - } - colors_.shrink_to_fit(); + for (int h = 0; h < reference_.size().height; h += thread_nbr) { + std::vector thread_list{}; + for (auto i = 0u; i < thread_nbr; ++i) { + thread_list.push_back( + std::thread(&ImageManipulator::threaded_get_color, this, h + i)); + } + std::for_each(thread_list.begin(), thread_list.end(), + [](auto &th) { th.join(); }); + } + colors_.shrink_to_fit(); } /** @@ -328,20 +307,20 @@ void ImageManipulator::get_color_set() */ void ImageManipulator::threaded_get_color(int const 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(); - } - } + 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(); + } + } } /** @@ -353,22 +332,21 @@ void ImageManipulator::threaded_get_color(int const t_h) * \param[in] t_size Size of the square * \param[in] t_color Color of the square */ -void ImageManipulator::draw_square(cv::Mat& t_img, - cv::Point const& t_top_left, +void ImageManipulator::draw_square(cv::Mat &t_img, cv::Point const &t_top_left, int const t_size, - cv::Scalar const& t_color) const + cv::Scalar const &t_color) const { - std::array points - = {t_top_left, cv::Point{t_top_left.x, t_top_left.y + t_size}, - cv::Point{t_top_left.x + t_size, t_top_left.y + t_size}, - cv::Point{t_top_left.x + t_size, t_top_left.y}}; - fillConvexPoly(t_img, points.data(), 4, t_color); + std::array points + = {t_top_left, cv::Point{t_top_left.x, t_top_left.y + t_size}, + cv::Point{t_top_left.x + t_size, t_top_left.y + t_size}, + cv::Point{t_top_left.x + t_size, t_top_left.y}}; + fillConvexPoly(t_img, points.data(), 4, t_color); } -void ImageManipulator::draw_shape(cv::Mat& t_img, cv::Scalar&& t_color) +void ImageManipulator::draw_shape(cv::Mat &t_img, cv::Scalar &&t_color) { - fillConvexPoly(t_img, shape_.get_points().data(), shape_.get_nb_points(), - t_color); + fillConvexPoly(t_img, shape_.get_points().data(), shape_.get_nb_points(), + t_color); } /** @@ -381,13 +359,13 @@ void ImageManipulator::draw_shape(cv::Mat& t_img, cv::Scalar&& t_color) * \param[in] t_img Image to replace \ref generated_image_ * \param[in] t_diff New euclidian distance */ -void ImageManipulator::update_gen_image(cv::Mat const& t_img, +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_); + diff_ = t_diff; + t_img.copyTo(generated_image_); + --remaining_iter_; + spdlog::debug("remaining iter: {}\tdiff: {}", remaining_iter_, diff_); } /** @@ -398,60 +376,60 @@ void ImageManipulator::update_gen_image(cv::Mat const& t_img, * \param t_tiles Collection of tiles to be merged together */ void ImageManipulator::merge_tiles( - std::vector> const& t_tiles) + std::vector> const &t_tiles) { - std::vector columns{}; - std::for_each(t_tiles.begin(), t_tiles.end(), [&columns](auto const& col) { - std::vector column_arr{}; - cv::Mat column_img{}; - std::for_each(col.begin(), col.end(), [&column_arr](auto const& tile) { - column_arr.push_back(tile.get_generated_image()); - }); - vconcat(column_arr, column_img); - columns.push_back(std::move(column_img)); - }); - hconcat(columns, generated_image_); + std::vector columns{}; + std::for_each(t_tiles.begin(), t_tiles.end(), [&columns](auto const &col) { + std::vector column_arr{}; + cv::Mat column_img{}; + std::for_each(col.begin(), col.end(), [&column_arr](auto const &tile) { + column_arr.push_back(tile.get_generated_image()); + }); + vconcat(column_arr, column_img); + columns.push_back(std::move(column_img)); + }); + hconcat(columns, generated_image_); } void ImageManipulator::method1() { - spdlog::debug("Beginning method1, initial difference: {}", diff_); - 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); - } - } + spdlog::debug("Beginning method1, initial difference: {}", diff_); + while (remaining_iter_ > 0 && diff_ > 0.0) { + auto temp_image = generated_image_.clone(); + create_shape(); + draw_shape(temp_image, 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("Running on {} threads", thread_nbr); - 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); - } - } + spdlog::debug("Beginning method2, initial difference: {}", diff_); + spdlog::debug("Running on {} threads", thread_nbr); + 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 method3, initial difference: {}", diff_); - spdlog::debug("Running on {} threads", thread_nbr); - 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); - } - } + spdlog::debug("Beginning method3, initial difference: {}", diff_); + spdlog::debug("Running on {} threads", thread_nbr); + 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); + } + } } /** @@ -459,36 +437,36 @@ void ImageManipulator::method3() */ void ImageManipulator::method4(bool const t_controlled_size) { - spdlog::debug("Beginning method4, initial difference: {}", diff_); - spdlog::debug("Running on {} threads", thread_nbr); - get_color_set(); - spdlog::debug("{} colors detected", colors_.size()); - while (remaining_iter_ > 0 && diff_ > 0.0) { - std::vector>>> - results{}; - std::vector> values{}; - // launch asynchronously candidate image generation - for (size_t i = 0; i < thread_nbr; ++i) { - results.push_back(std::async(std::launch::async, - &ImageManipulator::create_candidate, this, - t_controlled_size)); - } - // if candidate is a success, store it - std::for_each(results.begin(), results.end(), [&values, this](auto& elem) { - if (auto res = elem.get(); res.has_value() && res->second < this->diff_) { - values.push_back(*res); - } - }); - // apply best candidate - if (values.size() > 0) { - auto const pos - = std::min_element(std::begin(values), std::end(values), - [](const auto& elem1, const auto& elem2) { - return elem1.second < elem2.second; - }); - update_gen_image(pos->first, pos->second); - } - } + spdlog::debug("Beginning method4, initial difference: {}", diff_); + spdlog::debug("Running on {} threads", thread_nbr); + get_color_set(); + spdlog::debug("{} colors detected", colors_.size()); + while (remaining_iter_ > 0 && diff_ > 0.0) { + std::vector>>> + results{}; + std::vector> values{}; + // launch asynchronously candidate image generation + for (size_t i = 0; i < thread_nbr; ++i) { + results.push_back(std::async(std::launch::async, + &ImageManipulator::create_candidate, this, + t_controlled_size)); + } + // if candidate is a success, store it + std::for_each(results.begin(), results.end(), [&values, this](auto &elem) { + if (auto res = elem.get(); res.has_value() && res->second < this->diff_) { + values.push_back(*res); + } + }); + // apply best candidate + if (values.size() > 0) { + auto const pos + = std::min_element(std::begin(values), std::end(values), + [](const auto &elem1, const auto &elem2) { + return elem1.second < elem2.second; + }); + update_gen_image(pos->first, pos->second); + } + } } /** @@ -497,25 +475,23 @@ void ImageManipulator::method4(bool const t_controlled_size) * \param[in] t_rows Number of rows the reference should be divided into * \param[in] t_submethod Method to be used on each tile */ -void ImageManipulator::method5(bool const t_controlled_size, - int const t_cols, - int const t_rows, - int const t_submethod) +void ImageManipulator::method5(bool const t_controlled_size, int const t_cols, + int const t_rows, int const t_submethod) { - spdlog::debug("Beginning method5, initial difference: {}", diff_); - spdlog::debug("Running on {} threads", thread_nbr); + spdlog::debug("Beginning method5, initial difference: {}", diff_); + spdlog::debug("Running on {} threads", thread_nbr); - auto tiles = generate_tiles((t_cols != 0) ? t_cols : t_rows, t_rows); - spdlog::debug("{} tiles", tiles.size()); + auto tiles = generate_tiles((t_cols != 0) ? t_cols : t_rows, t_rows); + spdlog::debug("{} tiles", tiles.size()); - std::vector thread_list{}; - std::for_each(tiles.begin(), tiles.end(), [&](auto& row) { - std::for_each(row.begin(), row.end(), [&](auto& tile) { - thread_list.emplace_back( - [&]() { tile.exec_method(t_submethod, t_controlled_size); }); - }); - }); - std::for_each(thread_list.begin(), thread_list.end(), - [](auto& th) { th.join(); }); - merge_tiles(tiles); + std::vector thread_list{}; + std::for_each(tiles.begin(), tiles.end(), [&](auto &row) { + std::for_each(row.begin(), row.end(), [&](auto &tile) { + thread_list.emplace_back( + [&]() { tile.exec_method(t_submethod, t_controlled_size); }); + }); + }); + std::for_each(thread_list.begin(), thread_list.end(), + [](auto &th) { th.join(); }); + merge_tiles(tiles); } diff --git a/src/parseargs.cc b/src/parseargs.cc index 7bbee2b..9bacda2 100644 --- a/src/parseargs.cc +++ b/src/parseargs.cc @@ -1,10 +1,11 @@ #include "parseargs.hh" + #include #include constexpr int DEFAULT_ITERATIONS = 2000; -using path = std::filesystem::path; +using path = std::filesystem::path; namespace po = boost::program_options; /** @@ -19,17 +20,15 @@ namespace po = boost::program_options; * \param[out] t_input Input path * \param[out] t_output Output path */ -void processFilenames(po::variables_map const& t_vm, - path const& t_input, - path& t_output) +void processFilenames(po::variables_map const &t_vm, path const &t_input, + path &t_output) { - if (!t_vm.count("output")) { - t_output.replace_filename("output_" - + std::string{t_input.filename().string()}); - } - else if (!t_output.has_extension()) { - t_output.replace_extension(".png"); - } + if (!t_vm.count("output")) { + t_output.replace_filename("output_" + + std::string{t_input.filename().string()}); + } else if (!t_output.has_extension()) { + t_output.replace_extension(".png"); + } } /** @@ -42,62 +41,58 @@ void processFilenames(po::variables_map const& t_vm, * \param[in] t_av Arguments passed to the program * \return Tuple of path, path, int, int, int, int, int, bool and bool */ -[[nodiscard]] auto parse_args(int t_ac, char** t_av) -> ParsedArgs +[[nodiscard]] auto parse_args(int t_ac, char **t_av) -> ParsedArgs { - ParsedArgs ret{}; - po::options_description desc("Allowed options"); - desc.add_options()("help,h", "Display this help message")( - "input,i", po::value(), "Input image")( - "output,o", po::value(), - "Image output path (default: \"output_\" + input path)")( - "iterations,n", po::value(), "Number of iterations (default: 2000)")( - "method,m", po::value(), "Method number to be used (default: 1)")( - "form,f", po::value(), "Select shape (1:square, 2:triangle)")( - "cols,c", po::value(), - "For method 5 only, number of columns the reference image should be " - "divided into. If the value is equal to 0, then it will be assumed " - "there will be as many rows as there are collumns. (default: 0)")( - "rows,r", po::value(), - "For method 5 only, number of rows the reference image should be " - "divided into. (default: 1)")( - "submethod,S", po::value(), - "Sub-method that will be used to generate the individual tiles from " - "method 5. (default: 1)")("size,s", - "Enables controlled size of the random shapes")( - "verbose,v", "Enables verbosity"); - po::variables_map vm; - po::store(po::parse_command_line(t_ac, t_av, desc), vm); - po::notify(vm); - if (vm.count("help") || !vm.count("input")) { - std::cout << desc << "\n"; - std::exit(!vm.count("help")); - } + ParsedArgs ret{}; + po::options_description desc("Allowed options"); + desc.add_options() + ("help,h", "Display this help message") + ("input,i", po::value(), "Input image") + ("output,o", po::value(), + "Image output path (default: \"output_\" + input path)") + ("iterations,n", po::value(), "Number of iterations (default: 2000)") + ("method,m", po::value(), "Method number to be used (default: 1)") + ("form,f", po::value(), "Select shape (1:square, 2:triangle)") + ("cols,c", po::value(), + "For method 5 only, number of columns the reference image should be " + "divided into. If the value is equal to 0, then it will be assumed " + "there will be as many rows as there are collumns. (default: 0)") + ("rows,r", po::value(), + "For method 5 only, number of rows the reference image should be " + "divided into. (default: 1)") + ("submethod,S", po::value(), + "Sub-method that will be used to generate the individual tiles from " + "method 5. (default: 1)") + ("size,s", "Enables controlled size of the random shapes") + ("verbose,v", "Enables verbosity"); + po::variables_map vm; + po::store(po::parse_command_line(t_ac, t_av, desc), vm); + po::notify(vm); + if (vm.count("help") || !vm.count("input")) { + std::cout << desc << "\n"; + std::exit(!vm.count("help")); + } - auto const input_path = vm["input"].as(); - auto output_path - = vm.count("output") ? vm["output"].as() : input_path.filename(); - processFilenames(vm, input_path, output_path); + auto const input_path = vm["input"].as(); + auto output_path + = vm.count("output") ? vm["output"].as() : input_path.filename(); + processFilenames(vm, input_path, output_path); - ret.input_path = input_path; - ret.output_path = output_path; - ret.iterations = vm.count("iterations") ? vm["iterations"].as() - : DEFAULT_ITERATIONS; - ret.method = vm.count("method") ? vm["method"].as() : 1; - switch (vm.count("form") ? vm["form"].as() : 1) { - case 2: - ret.shape= Shape::ShapeType::Triangle; - break; - case 1: - [[fallthrough]]; - default: - ret.shape = Shape::ShapeType::Square; - break; - } + ret.input_path = input_path; + ret.output_path = output_path; + ret.iterations = vm.count("iterations") ? vm["iterations"].as() + : DEFAULT_ITERATIONS; + ret.method = vm.count("method") ? vm["method"].as() : 1; + switch (vm.count("form") ? vm["form"].as() : 1) { + case 2: ret.shape = Shape::ShapeType::Triangle; break; + case 1: [[fallthrough]]; + default: ret.shape = Shape::ShapeType::Square; break; + } - ret.cols = vm.count("cols") ? vm["cols"].as() : 0; - ret.rows = vm.count("rows") ? vm["rows"].as() : 1; - ret.submethod = vm.count("submethod") ? vm["submethod"].as() : 1; - ret.controlled_size = vm.count("size"); - ret.verbose = vm.count("verbose"); - return ret; + ret.cols = vm.count("cols") ? vm["cols"].as() : 0; + ret.rows = vm.count("rows") ? vm["rows"].as() : 1; + ret.submethod = vm.count("submethod") ? vm["submethod"].as() : 1; + ret.controlled_size = vm.count("size"); + ret.verbose = vm.count("verbose"); + return ret; } diff --git a/src/shapes.cc b/src/shapes.cc index 5963f22..7660679 100644 --- a/src/shapes.cc +++ b/src/shapes.cc @@ -1,4 +1,5 @@ #include "shapes.hh" + #include #include @@ -6,22 +7,18 @@ using point_arr = std::array; Shape::Shape(Shape::ShapeType const t_type) : type_{t_type} { - switch (t_type) { - case ShapeType::Triangle: { - nb_points_ = 3; - break; - } - case ShapeType::Square: - [[fallthrough]]; - default: - nb_points_ = 4; - break; - } + switch (t_type) { + case ShapeType::Triangle: { + nb_points_ = 3; + break; + } + case ShapeType::Square: [[fallthrough]]; + default: nb_points_ = 4; break; + } } -Shape::Shape(Shape&& other) noexcept - : type_{std::move(other.type_)}, - points_{std::move(other.points_)}, +Shape::Shape(Shape &&other) noexcept + : type_{std::move(other.type_)}, points_{std::move(other.points_)}, nb_points_{std::move(other.nb_points_)} { } @@ -34,39 +31,36 @@ Shape::Shape(Shape&& other) noexcept * for * \return Array of points describing the shape */ -void Shape::operator()(cv::Point&& t_max_pos, - int const t_max_size, +void Shape::operator()(cv::Point &&t_max_pos, int const t_max_size, int const t_min_size) noexcept { - int const size = (rand() % (t_max_size - t_min_size)) + t_min_size; - cv::Point const top_left - = {rand() % (t_max_pos.x - size), rand() % (t_max_pos.y - size)}; - if (type_ == ShapeType::Triangle) { - create_triangle_points(top_left, size); - } - else { // ShapeType::Square - create_square_points(top_left, size); - } + int const size = (rand() % (t_max_size - t_min_size)) + t_min_size; + cv::Point const top_left + = {rand() % (t_max_pos.x - size), rand() % (t_max_pos.y - size)}; + if (type_ == ShapeType::Triangle) { + create_triangle_points(top_left, size); + } else { // ShapeType::Square + create_square_points(top_left, size); + } } - -void Shape::create_triangle_points(cv::Point const& t_top_left, +void Shape::create_triangle_points(cv::Point const &t_top_left, int const t_size) noexcept { - bool top_left = rand() % 1 == 0; - points_ = { - cv::Point{top_left ? t_top_left.x : t_top_left.x + t_size, t_top_left.y}, - cv::Point{top_left ? t_top_left.x + t_size : t_top_left.x, - t_top_left.y + t_size}, - cv::Point{t_top_left.x + rand() % t_size, t_top_left.y + t_size}, - cv::Point{0, 0}}; + bool top_left = rand() % 1 == 0; + points_ = { + cv::Point{top_left ? t_top_left.x : t_top_left.x + t_size, t_top_left.y}, + cv::Point{top_left ? t_top_left.x + t_size : t_top_left.x, + t_top_left.y + t_size}, + cv::Point{t_top_left.x + rand() % t_size, t_top_left.y + t_size}, + cv::Point{0, 0}}; } -void Shape::create_square_points(cv::Point const& t_top_left, +void Shape::create_square_points(cv::Point const &t_top_left, int const t_size) noexcept { - points_ = {cv::Point{t_top_left.x, t_top_left.y}, - cv::Point{t_top_left.x, t_top_left.y + t_size}, - cv::Point{t_top_left.x + t_size, t_top_left.y}, - cv::Point{t_top_left.x + t_size, t_top_left.y}}; + points_ = {cv::Point{t_top_left.x, t_top_left.y}, + cv::Point{t_top_left.x, t_top_left.y + t_size}, + cv::Point{t_top_left.x + t_size, t_top_left.y}, + cv::Point{t_top_left.x + t_size, t_top_left.y}}; }