Beginning refactoring, adding triangles to the list of possible shapes

This commit is contained in:
Phuntsok Drak-pa 2019-04-27 15:45:39 +02:00
parent 326eb7c9e8
commit 012508e523
7 changed files with 281 additions and 75 deletions

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <opencv2/highgui/highgui.hpp> #include "shapes.hh"
#include <opencv2/imgproc.hpp> #include <array>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -19,11 +19,13 @@ class ImageManipulator {
/// \brief Load image from input, and prepare for output /// \brief Load image from input, and prepare for output
ImageManipulator(std::string const t_input_path, ImageManipulator(std::string const t_input_path,
std::string const t_output_path, std::string const t_output_path,
int const iterations); int const iterations,
Shape::ShapeType const t_shape);
/// \brief Basically makes views from image /// \brief Basically makes views from image
ImageManipulator(cv::Mat const& t_origin_image, ImageManipulator(cv::Mat const& t_origin_image,
int const iterations, int const t_iterations,
Shape::ShapeType const t_shape,
int const t_x, int const t_x,
int const t_y, int const t_y,
int const t_width, int const t_width,
@ -67,41 +69,59 @@ class ImageManipulator {
/// \brief Calculates the euclidian distance between two images /// \brief Calculates the euclidian distance between two images
[[nodiscard]] auto euclidian_distance(cv::Mat const& t_img) const noexcept [[nodiscard]] auto euclidian_distance(cv::Mat const& t_img) const noexcept
-> double; -> double;
/// \brief Creates and returns a random color /// \brief Creates and returns a random color
[[nodiscard]] auto random_color() const noexcept; [[nodiscard]] auto random_color() const noexcept;
/// \brief Generates random square coordinates /// \brief Generates random square coordinates
[[nodiscard]] auto get_square_values() const noexcept; [[deprecated]] [[nodiscard]] auto get_square_values() const noexcept;
/// \brief Generates controlled random square coordinates /// \brief Generates controlled random square coordinates
[[nodiscard]] auto get_controlled_square_values() const noexcept; [[deprecated]] [[nodiscard]] auto get_controlled_square_values() const
noexcept;
/// \brief Generates a candidate for image generation improvement /// \brief Generates a candidate for image generation improvement
[[nodiscard]] auto create_candidate(bool const t_controlled_size) const; [[nodiscard]] auto create_candidate(bool const t_controlled_size);
/// \brief Generates organized views of the reference image for method 5 /// \brief Generates organized views of the reference image for method 5
[[nodiscard]] auto generate_tiles(int const t_cols, int const t_rows) const; [[nodiscard]] auto generate_tiles(int const t_cols, int const t_rows) const;
/// \brief Gets all colors from the reference image /// \brief Gets all colors from the reference image
void get_color_set(); void get_color_set();
/// \brief Threaded helper for \ref get_color_set /// \brief Threaded helper for \ref get_color_set
void threaded_get_color(int const t_h); void threaded_get_color(int const t_h);
/// \brief Draw a square on an image /// \brief Draw a square on an image
void draw_square(cv::Mat& t_img, [[deprecated]] void draw_square(cv::Mat& t_img,
cv::Point const& t_top_left, cv::Point const& t_top_left,
int const t_size, int const t_size,
cv::Scalar const& t_color) const; cv::Scalar const& t_color) const;
void draw_shape(cv::Mat& t_img, cv::Scalar&& t_color);
/// \brief Update this objects generated image /// \brief Update this objects generated image
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);
/// \brief Merges tiles generated by method5 /// \brief Merges tiles generated by method5
void merge_tiles(std::vector<std::vector<ImageManipulator>> const& t_tiles); void merge_tiles(std::vector<std::vector<ImageManipulator>> const& t_tiles);
/// \brief First method as described in the /// \brief First method as described in the
/// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf)
void method1(); void method1();
/// \brief Second method as described in the /// \brief Second method as described in the
/// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf)
void method2(); void method2();
/// \brief Third method as described in the /// \brief Third method as described in the
/// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf)
void method3(); void method3();
/// \brief Fourth method as described in the /// \brief Fourth method as described in the
/// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf)
void method4(bool const t_controlled_size); void method4(bool const t_controlled_size);
/// \brief Fifth method as described in the /// \brief Fifth method as described in the
/// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf) /// [report](https://labs.phundrak.fr/phundrak/genetic-images/blob/master/report/report.pdf)
void method5(bool const t_controlled_size, void method5(bool const t_controlled_size,
@ -116,6 +136,7 @@ class ImageManipulator {
cv::Mat generated_image_{ cv::Mat generated_image_{
reference_.size().height, reference_.size().width, CV_8UC3, reference_.size().height, reference_.size().width, CV_8UC3,
cv::Scalar(0, 0, 0)}; /*!< Working, generated image */ cv::Scalar(0, 0, 0)}; /*!< Working, generated image */
Shape shape_{Shape::ShapeType::Square};
mutable std::mutex mutable std::mutex
colors_mutex_{}; /*!< Thread mutex for color set generation */ colors_mutex_{}; /*!< Thread mutex for color set generation */
std::string const output_path_{}; /*!< Write path for the generated image */ std::string const output_path_{}; /*!< Write path for the generated image */

View File

@ -1,15 +1,20 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include <tuple> #include "shapes.hh"
struct ParsedArgs {
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;
};
/// \brief Parses the arguments passed to the program /// \brief Parses the arguments passed to the program
[[nodiscard]] auto parse_args(int, char**) -> std::tuple<std::filesystem::path, [[nodiscard]] auto parse_args(int, char**) -> ParsedArgs;
std::filesystem::path,
int,
int,
int,
int,
int,
bool,
bool>;

63
include/genimg/shapes.hh Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <array>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
class Shape {
public:
static constexpr int MAX_POINTS{4};
enum class ShapeType { Square, Triangle };
/// \brief Default constructor
Shape() = delete;
Shape(Shape::ShapeType const t_type);
/// \brief Copy constructor
Shape(const Shape& other) = default;
/// \brief Move constructor
Shape(Shape&& other) noexcept;
/// \brief Destructor
virtual ~Shape() noexcept = default;
/// \brief Copy assignment operator
Shape& operator=(const Shape& other) = 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;
[[nodiscard]] auto get_points() const noexcept
-> std::array<cv::Point, Shape::MAX_POINTS> const&
{
return points_;
}
/// \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_; }
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<cv::Point, Shape::MAX_POINTS> points_{
cv::Point{0, 0}, cv::Point{0, 0}, cv::Point{0, 0}, cv::Point{0, 0}};
int nb_points_{Shape::MAX_POINTS};
};

View File

@ -7,16 +7,18 @@
int main(int ac, char** av) int main(int ac, char** av)
{ {
std::srand(std::time(nullptr)); std::srand(std::time(nullptr));
auto const [input_file, output_file, iterations, method, cols, rows, auto const arguments = parse_args(ac, av);
submethod, controlled_size, verbose] spdlog::set_level(arguments.verbose ? spdlog::level::debug
= parse_args(ac, av); : spdlog::level::info);
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("Input file:\t{}", arguments.input_path.native());
spdlog::debug("Output file:\t{}", output_file.native()); spdlog::debug("Output file:\t{}", arguments.output_path.native());
spdlog::debug("Iterations:\t{}", iterations); spdlog::debug("Iterations:\t{}", arguments.iterations);
ImageManipulator image_process{input_file, output_file, iterations}; ImageManipulator image_process{arguments.input_path, arguments.output_path,
image_process.exec_method(method, controlled_size, cols, rows, submethod); arguments.iterations, arguments.shape};
image_process.exec_method(arguments.method, arguments.controlled_size,
arguments.cols, arguments.rows,
arguments.submethod);
image_process.write_file(); image_process.write_file();
} }

View File

@ -1,6 +1,5 @@
#include "methods.hh" #include "methods.hh"
#include <algorithm> #include <algorithm>
#include <array>
#include <future> #include <future>
#include <optional> #include <optional>
#include <thread> #include <thread>
@ -22,6 +21,7 @@ ImageManipulator::ImageManipulator(const ImageManipulator& other)
: colors_{other.colors_}, : colors_{other.colors_},
reference_{other.reference_}, reference_{other.reference_},
generated_image_{other.generated_image_}, generated_image_{other.generated_image_},
shape_{other.shape_},
output_path_{other.output_path_}, output_path_{other.output_path_},
diff_{other.diff_}, diff_{other.diff_},
total_iterations_{other.total_iterations_}, total_iterations_{other.total_iterations_},
@ -41,6 +41,7 @@ ImageManipulator::ImageManipulator(ImageManipulator&& other) noexcept
: colors_{std::move(other.colors_)}, : colors_{std::move(other.colors_)},
reference_{std::move(other.reference_)}, reference_{std::move(other.reference_)},
generated_image_{std::move(other.generated_image_)}, generated_image_{std::move(other.generated_image_)},
shape_{std::move(other.shape_)},
output_path_{std::move(other.output_path_)}, output_path_{std::move(other.output_path_)},
diff_{std::move(other.diff_)}, diff_{std::move(other.diff_)},
total_iterations_{other.total_iterations_}, total_iterations_{other.total_iterations_},
@ -60,8 +61,10 @@ ImageManipulator::ImageManipulator(ImageManipulator&& other) noexcept
*/ */
ImageManipulator::ImageManipulator(std::string const t_input_path, ImageManipulator::ImageManipulator(std::string const t_input_path,
std::string const t_output_path, std::string const t_output_path,
int const t_iterations) int const t_iterations,
Shape::ShapeType const t_shape)
: reference_{cv::imread(t_input_path, cv::IMREAD_COLOR)}, : reference_{cv::imread(t_input_path, cv::IMREAD_COLOR)},
shape_{Shape{t_shape}},
output_path_{t_output_path}, output_path_{t_output_path},
total_iterations_{t_iterations} total_iterations_{t_iterations}
{ {
@ -84,6 +87,7 @@ ImageManipulator::ImageManipulator(std::string const t_input_path,
*/ */
ImageManipulator::ImageManipulator(cv::Mat const& t_origin_image, ImageManipulator::ImageManipulator(cv::Mat const& t_origin_image,
int const t_iterations, int const t_iterations,
Shape::ShapeType const t_shape,
int const t_x, int const t_x,
int const t_y, int const t_y,
int const t_width, int const t_width,
@ -91,6 +95,7 @@ ImageManipulator::ImageManipulator(cv::Mat const& t_origin_image,
: reference_{t_origin_image( : reference_{t_origin_image(
cv::Range{t_y, std::min(t_y + t_height, t_origin_image.rows)}, 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)})}, cv::Range{t_x, std::min(t_x + t_width, t_origin_image.cols)})},
shape_{Shape{t_shape}},
total_iterations_{t_iterations} total_iterations_{t_iterations}
{ {
if (!reference_.data) { if (!reference_.data) {
@ -229,21 +234,39 @@ void ImageManipulator::exec_method(int const t_nb_method,
* \return Optional pair of cv::Mat and double * \return Optional pair of cv::Mat and double
*/ */
[[nodiscard]] auto ImageManipulator::create_candidate( [[nodiscard]] auto ImageManipulator::create_candidate(
bool const t_controlled_size = false) const bool const t_controlled_size = false)
{ {
auto temp_image = generated_image_.clone(); auto temp_img = generated_image_.clone();
auto const [rand_x, rand_y, size] = t_controlled_size auto create_shape = [&]() {
? get_controlled_square_values() return shape_(cv::Point{reference_.size().width, reference_.size().height},
: get_square_values(); std::min(reference_.size().width, reference_.size().height));
};
auto create_controlled_shape = [&]() {
float const coef = static_cast<float>(remaining_iter_)
/ static_cast<float>(total_iterations_);
int const min_size = static_cast<int>(
(static_cast<float>(
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()]; auto const& color = colors_[rand() % colors_.size()];
draw_square( if (t_controlled_size) {
temp_image, cv::Point{rand_x, rand_y}, size, create_shape();
cv::Scalar{static_cast<double>(color[0]), static_cast<double>(color[1]), }
static_cast<double>(color[2])}); else {
auto new_diff = euclidian_distance(temp_image); create_controlled_shape();
}
draw_shape(temp_img, cv::Scalar{static_cast<double>(color[0]),
static_cast<double>(color[1]),
static_cast<double>(color[2])});
auto new_diff = euclidian_distance(temp_img);
return (new_diff < diff_) return (new_diff < diff_)
? std::optional<std::pair<cv::Mat, double>>{std::make_pair( ? std::optional<std::pair<cv::Mat, double>>{std::make_pair(
std::move(temp_image), new_diff)} std::move(temp_img), new_diff)}
: std::nullopt; : std::nullopt;
} }
@ -270,8 +293,9 @@ void ImageManipulator::exec_method(int const t_nb_method,
int const height = (index_y != t_rows - 1) int const height = (index_y != t_rows - 1)
? tile_height ? tile_height
: tile_height + reference_.rows % tile_height; : tile_height + reference_.rows % tile_height;
tile_col.emplace_back(reference_, total_iterations_, index_x * tile_width, tile_col.emplace_back(reference_, total_iterations_, shape_.get_type(),
index_y * tile_height, width, height); index_x * tile_width, index_y * tile_height, width,
height);
} }
tiles.push_back(tile_col); tiles.push_back(tile_col);
} }
@ -341,6 +365,12 @@ void ImageManipulator::draw_square(cv::Mat& t_img,
fillConvexPoly(t_img, points.data(), 4, t_color); fillConvexPoly(t_img, points.data(), 4, 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);
}
/** /**
* Updates the objects current generated image and difference with its * Updates the objects current generated image and difference with its
* reference by replacing them with the arguments passed in this function. This * reference by replacing them with the arguments passed in this function. This

View File

@ -42,29 +42,29 @@ void processFilenames(po::variables_map const& t_vm,
* \param[in] t_av Arguments passed to the program * \param[in] t_av Arguments passed to the program
* \return Tuple of path, path, int, int, int, int, int, bool and bool * \return Tuple of path, path, int, int, int, int, int, bool and bool
*/ */
[[nodiscard]] auto parse_args(int t_ac, char** t_av) [[nodiscard]] auto parse_args(int t_ac, char** t_av) -> ParsedArgs
-> std::tuple<path, path, int, int, int, int, int, bool, bool>
{ {
ParsedArgs ret{};
po::options_description desc("Allowed options"); po::options_description desc("Allowed options");
desc.add_options() desc.add_options()("help,h", "Display this help message")(
("help,h", "Display this help message") "input,i", po::value<path>(), "Input image")(
("input,i", po::value<path>(), "Input image") "output,o", po::value<path>(),
("output,o", po::value<path>(), "Image output path (default: \"output_\" + input path)")(
"Image output path (default: \"output_\" + input path)") "iterations,n", po::value<int>(), "Number of iterations (default: 2000)")(
("iterations,n", po::value<int>(), "Number of iterations (default: 2000)") "method,m", po::value<int>(), "Method number to be used (default: 1)")(
("method,m", po::value<int>(), "Method number to be used (default: 1)") "form,f", po::value<int>(), "Select shape (1:square, 2:triangle)")(
("cols,c", po::value<int>(), "cols,c", po::value<int>(),
"For method 5 only, number of columns the reference image should be " "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 " "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)") "there will be as many rows as there are collumns. (default: 0)")(
("rows,r", po::value<int>(), "rows,r", po::value<int>(),
"For method 5 only, number of rows the reference image should be " "For method 5 only, number of rows the reference image should be "
"divided into. (default: 1)") "divided into. (default: 1)")(
("submethod,S", po::value<int>(), "submethod,S", po::value<int>(),
"Sub-method that will be used to generate the individual tiles from " "Sub-method that will be used to generate the individual tiles from "
"method 5. (default: 1)") "method 5. (default: 1)")("size,s",
("size,s", "Enables controlled size of the random shapes") "Enables controlled size of the random shapes")(
("verbose,v", "Enables verbosity"); "verbose,v", "Enables verbosity");
po::variables_map vm; po::variables_map vm;
po::store(po::parse_command_line(t_ac, t_av, desc), vm); po::store(po::parse_command_line(t_ac, t_av, desc), vm);
po::notify(vm); po::notify(vm);
@ -78,13 +78,26 @@ void processFilenames(po::variables_map const& t_vm,
= vm.count("output") ? vm["output"].as<path>() : input_path.filename(); = vm.count("output") ? vm["output"].as<path>() : input_path.filename();
processFilenames(vm, input_path, output_path); processFilenames(vm, input_path, output_path);
return std::make_tuple( ret.input_path = input_path;
input_path, output_path, ret.output_path = output_path;
vm.count("iterations") ? vm["iterations"].as<int>() : DEFAULT_ITERATIONS, ret.iterations = vm.count("iterations") ? vm["iterations"].as<int>()
vm.count("method") ? vm["method"].as<int>() : 1, : DEFAULT_ITERATIONS;
vm.count("cols") ? vm["cols"].as<int>() : 0, ret.method = vm.count("method") ? vm["method"].as<int>() : 1;
vm.count("rows") ? vm["rows"].as<int>() : 1, switch (vm.count("form") ? vm["form"].as<int>() : 1) {
vm.count("submethod") ? vm["submethod"].as<int>() : 1, case 2:
vm.count("size"), ret.shape= Shape::ShapeType::Triangle;
vm.count("verbose")); break;
case 1:
[[fallthrough]];
default:
ret.shape = Shape::ShapeType::Square;
break;
}
ret.cols = vm.count("cols") ? vm["cols"].as<int>() : 0;
ret.rows = vm.count("rows") ? vm["rows"].as<int>() : 1;
ret.submethod = vm.count("submethod") ? vm["submethod"].as<int>() : 1;
ret.controlled_size = vm.count("size");
ret.verbose = vm.count("verbose");
return ret;
} }

72
src/shapes.cc Normal file
View File

@ -0,0 +1,72 @@
#include "shapes.hh"
#include <cmath>
#include <utility>
using point_arr = std::array<cv::Point, 4>;
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;
}
}
Shape::Shape(Shape&& other) noexcept
: type_{std::move(other.type_)},
points_{std::move(other.points_)},
nb_points_{std::move(other.nb_points_)}
{
}
/**
* Generates all the needed points for the corresponding shape described in
* \ref type_.
*
* \param t_max_pos Bottom-rightmost point of the image the shape is generated
* for
* \return Array of points describing the shape
*/
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);
}
}
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}};
}
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}};
}