diff --git a/include/genimg/methods.hh b/include/genimg/methods.hh index fde0c0b..d7d5509 100644 --- a/include/genimg/methods.hh +++ b/include/genimg/methods.hh @@ -13,24 +13,34 @@ class ImageManipulator { public: ImageManipulator() = delete; - ImageManipulator(const ImageManipulator& other) = delete; - ImageManipulator(ImageManipulator&& other) noexcept = delete; - ImageManipulator& operator=(const ImageManipulator& other) = delete; - ImageManipulator& operator=(ImageManipulator&& other) noexcept = delete; + ImageManipulator(const ImageManipulator& other); + ImageManipulator(ImageManipulator&& other) noexcept; + [[nodiscard]] auto operator=(const ImageManipulator& other) + -> ImageManipulator; + [[nodiscard]] auto operator=(ImageManipulator&& other) noexcept + -> ImageManipulator; // Load image from input, and prepare for output ImageManipulator(std::filesystem::path const t_input_path, std::filesystem::path const t_output_path, int const iterations); - // ImageManipulator(cv::Mat const& t_origin_image, - // int const iterations, - // int const t_x, - // int const t_y, - // int const t_width, - // int const t_height); + ImageManipulator(cv::Mat const& t_origin_image, + int const iterations, + int const t_x, + int const t_y, + int const t_width, + int const t_height); - void exec_method(int const t_nb_method, bool const t_controlled_size); + 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); void write_file() const; + [[nodiscard]] auto const& get_generated_image() const noexcept + { + return generated_image_; + } //! Destructor virtual ~ImageManipulator() noexcept = default; @@ -51,21 +61,27 @@ class ImageManipulator { cv::Point const& t_top_left, int const t_size, cv::Scalar const& t_color) const; - void update_gen_image(cv::Mat const& t_img, double const t_diff); + void update_gen_image(cv::Mat const& t_img, double const t_diff); void method1(); void method2(); void method3(); void method4(bool const t_controlled_size); + void method5(bool const t_controlled_size, + int cols, + int const rows, + int const submethod); std::vector> colors_ = std::vector>{}; cv::Mat const reference_; - cv::Mat generated_image_; - std::mutex colors_mutex_ = std::mutex{}; - std::string const output_path_; + cv::Mat generated_image_ + = cv::Mat{reference_.size().height, reference_.size().width, CV_8UC3, + cv::Scalar(0, 0, 0)}; + mutable std::mutex colors_mutex_ = std::mutex{}; + std::string const output_path_{""}; double diff_ = 0.0; int const total_iterations_ = 0; - int remaining_iter_ = 0; - int const width_; - int const height_; + int remaining_iter_ = total_iterations_; + int const width_ = reference_.size().width; + int const height_ = reference_.size().height; }; diff --git a/include/genimg/parseargs.hh b/include/genimg/parseargs.hh index 51df852..3535547 100644 --- a/include/genimg/parseargs.hh +++ b/include/genimg/parseargs.hh @@ -1,5 +1,4 @@ -#ifndef GENETIC_IMAGE_INCLUDE_GENIMG_PARSEARGS_HH_ -#define GENETIC_IMAGE_INCLUDE_GENIMG_PARSEARGS_HH_ +#pragma once #include #include @@ -10,7 +9,6 @@ int, int, int, + int, bool, bool>; - -#endif /* GENETIC_IMAGE_INCLUDE_GENIMG_PARSEARGS_HH_ */ diff --git a/src/main.cc b/src/main.cc index f13de5f..9a940f1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -7,19 +7,16 @@ int main(int ac, char** av) { std::srand(std::time(nullptr)); - auto [input_file, output_file, iterations, method, columns, rows, - controlled_size, verbose] + auto const [input_file, output_file, iterations, method, cols, rows, + submethod, controlled_size, verbose] = parse_args(ac, av); - if (rows == 0) { - rows = columns; - } spdlog::set_level(verbose ? spdlog::level::debug : spdlog::level::info); - spdlog::set_pattern("[thread %t] %+"); + spdlog::set_pattern("[thread %t] %+"); spdlog::debug("Input file:\t{}", input_file.native()); spdlog::debug("Output file:\t{}", output_file.native()); spdlog::debug("Iterations:\t{}", iterations); ImageManipulator image_process{input_file, output_file, iterations}; - image_process.exec_method(method, controlled_size); + image_process.exec_method(method, controlled_size, cols, rows, submethod); image_process.write_file(); } diff --git a/src/methods.cc b/src/methods.cc index fcd0bfd..a00c2ce 100644 --- a/src/methods.cc +++ b/src/methods.cc @@ -8,7 +8,6 @@ #include #include -std::mutex colors_mutex; auto const thread_nbr = std::thread::hardware_concurrency(); /////////////////////////////////////////////////////////////////////////////// @@ -20,15 +19,9 @@ 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} + total_iterations_{t_iterations} { if (!reference_.data) { spdlog::critical("Could not open or find image!\n"); @@ -39,9 +32,72 @@ ImageManipulator::ImageManipulator(std::filesystem::path const t_input_path, spdlog::debug("Height:\t{}", reference_.size().height); } +ImageManipulator::ImageManipulator(cv::Mat const& t_origin_image, + int const t_iterations, + 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)})}, + diff_{euclidian_distance(generated_image_)}, + total_iterations_{t_iterations} +{ + 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); +} + +ImageManipulator::ImageManipulator(const ImageManipulator& other) + : colors_{other.colors_}, + reference_{other.reference_}, + generated_image_{other.generated_image_}, + output_path_{other.output_path_}, + diff_{other.diff_}, + total_iterations_{other.total_iterations_}, + remaining_iter_{other.remaining_iter_}, + width_{other.width_}, + height_{other.height_} +{ +} + +ImageManipulator::ImageManipulator(ImageManipulator&& other) noexcept + : colors_{std::move(other.colors_)}, + reference_{std::move(other.reference_)}, + generated_image_{std::move(other.generated_image_)}, + 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_}, + height_{other.height_} +{ +} + +// operators ////////////////////////////////////////////////////////////////// +[[nodiscard]] auto ImageManipulator::operator=(const ImageManipulator& other) + -> ImageManipulator +{ + return ImageManipulator(other); +} + +[[nodiscard]] auto ImageManipulator::operator=( + ImageManipulator&& other) noexcept -> ImageManipulator +{ + return ImageManipulator{std::move(other)}; +} + // public methods ///////////////////////////////////////////////////////////// void ImageManipulator::exec_method(int const t_nb_method, - bool const t_controlled_size = false) + bool const t_controlled_size = false, + int const cols = 1, + int const rows = 0, + int const submethod = 1) { switch (t_nb_method) { case 1: { @@ -60,6 +116,10 @@ void ImageManipulator::exec_method(int const t_nb_method, method4(t_controlled_size); break; } + case 5: { + method5(t_controlled_size, cols, rows, submethod); + break; + } default: spdlog::error("Requested method {} is not implemented.", t_nb_method); std::exit(-1); @@ -138,10 +198,9 @@ void ImageManipulator::write_file() const void ImageManipulator::get_color_set() { - for (int h = 0; h < reference_.size().height; - h += std::thread::hardware_concurrency()) { + for (int h = 0; h < reference_.size().height; h += thread_nbr) { std::vector thread_list{}; - for (auto i = 0u; i < std::thread::hardware_concurrency(); ++i) { + for (auto i = 0u; i < thread_nbr; ++i) { thread_list.push_back( std::thread(&ImageManipulator::threaded_get_color, this, h + i)); } @@ -226,7 +285,7 @@ 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()); + spdlog::debug("Running on {} threads", thread_nbr); get_color_set(); spdlog::debug("{} colors detected", colors_.size()); while (remaining_iter_ > 0 && diff_ > 0.0) { @@ -238,10 +297,10 @@ void ImageManipulator::method2() void ImageManipulator::method3() { - spdlog::debug("Beginning method2, initial difference: {}", diff_); + spdlog::debug("Beginning method3, initial difference: {}", diff_); spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, remaining_iter_); - spdlog::debug("Running on {} threads", std::thread::hardware_concurrency()); + spdlog::debug("Running on {} threads", thread_nbr); get_color_set(); spdlog::debug("{} colors detected", colors_.size()); while (remaining_iter_ > 0 && diff_ > 0.0) { @@ -254,35 +313,66 @@ void ImageManipulator::method3() void ImageManipulator::method4(bool const t_controlled_size) { - spdlog::debug("Beginning method2, initial difference: {}", diff_); + spdlog::debug("Beginning method4, initial difference: {}", diff_); spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, remaining_iter_); - spdlog::debug("Running on {} threads", std::thread::hardware_concurrency()); + spdlog::debug("Running on {} threads", thread_nbr); get_color_set(); spdlog::debug("{} colors detected", colors_.size()); - while(remaining_iter_ > 0 && diff_ > 0.0) - { + while (remaining_iter_ > 0 && diff_ > 0.0) { std::vector>>> results{}; std::vector> values{}; - for (size_t i = 0; i < std::thread::hardware_concurrency(); ++i) { + for (size_t i = 0; i < thread_nbr; ++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); - } + 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); + } } } + +void ImageManipulator::method5(bool const t_controlled_size, + int cols, + int const rows, + int const submethod) +{ + spdlog::debug("Beginning method5, initial difference: {}", diff_); + spdlog::debug("nb_total_iter: {}, nb_remaining_iter: {}", total_iterations_, + remaining_iter_); + spdlog::debug("Running on {} threads", thread_nbr); + if (cols == 0) { + cols = rows; + } + std::vector> tiles{}; + int const tile_width = reference_.cols / cols; + int const tile_height = reference_.rows / rows; + spdlog::debug("tile: width:{}\theight:{}", tile_width, tile_height); + spdlog::debug("image: width:{}\theight:{}", reference_.cols, reference_.rows); + for (int i = 0; i < rows; ++i) { + std::vector tile_row{}; + for (int j = 0; j < cols; ++j) { + tile_row.emplace_back( + reference_, total_iterations_, i * tile_height, j * tile_width, + (i != rows - 1) ? tile_height + : tile_height + reference_.cols % tile_height, + (j != cols - 1) ? tile_width + : tile_width + reference_.cols % tile_width); + } + tiles.push_back(tile_row); + } + spdlog::debug("{} tiles", tiles.size()); +} diff --git a/src/parseargs.cc b/src/parseargs.cc index 26c959f..109659e 100644 --- a/src/parseargs.cc +++ b/src/parseargs.cc @@ -22,7 +22,7 @@ void processFilenames(po::variables_map const& vm, } [[nodiscard]] auto parse_args(int t_ac, char** t_av) - -> std::tuple + -> std::tuple { po::options_description desc("Allowed options"); desc.add_options() @@ -32,13 +32,16 @@ void processFilenames(po::variables_map const& vm, "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)") - ("columns,c", po::value(), + ("cols,c", po::value(), "For method 5 only, number of columns the reference image should be " - "divided into. (default: 1)") - ("rows,r", po::value(), - "For method 5 only, number of rows 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; @@ -58,7 +61,9 @@ void processFilenames(po::variables_map const& vm, input_path, output_path, vm.count("iterations") ? vm["iterations"].as() : DEFAULT_ITERATIONS, vm.count("method") ? vm["method"].as() : 1, - vm.count("column") ? vm["column"].as() : 1, - vm.count("rows") ? vm["rows"].as() : 0, vm.count("size"), + vm.count("cols") ? vm["cols"].as() : 0, + vm.count("rows") ? vm["rows"].as() : 1, + vm.count("submethod") ? vm["submethod"].as() : 1, + vm.count("size"), vm.count("verbose")); }