genetic-images/src/methods.cc

197 lines
6.5 KiB
C++

#include "methods.hh"
#include "common.hh"
#include "drawing.hh"
#include <algorithm>
#include <array>
#include <cstdlib>
#include <optional>
#include <thread>
#include <utility>
#include <vector>
auto const thread_nbr = std::thread::hardware_concurrency();
std::mutex numbers_mutex;
using randint = std::uniform_int_distribution<>;
using Color = std::array<uchar, 3>;
using ColorSet = std::vector<Color>;
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<std::thread> 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<int, int, int>(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)
{
auto temp_image = t_base.clone();
auto const [rand_x, rand_y, size]
= 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::pair<cv::Mat, double>>{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<uchar>(t_h, w), t_reference.at<uchar>(t_h, w + 1),
t_reference.at<uchar>(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<double>(t_color[0]),
static_cast<double>(t_color[1]),
static_cast<double>(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, false);
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();
int const rand_x = rand() % temp_image.size().width;
int const rand_y = rand() % temp_image.size().height;
float const coef
= static_cast<float>(t_iterations) / static_cast<float>(init_iter);
int const min_size = static_cast<int>(
(static_cast<float>(
std::min(t_reference.size().width, t_reference.size().height))
/ 2.0f)
* coef);
int const max_size = min_size * 2 + 1;
int const size = rand() % (max_size - min_size) + min_size;
methods_private::newSquare2(temp_image, cv::Point{rand_x, rand_y}, size,
colors[rand() % colors.size()]);
if (auto new_diff = euclidian_distance(t_reference, temp_image);
new_diff < diff) {
diff = new_diff;
temp_image.copyTo(t_output);
spdlog::debug("iteration:{} diff:{} size: {} coef:{} min:{} max:{}",
t_iterations, diff, size, coef, min_size, max_size);
--t_iterations;
}
}
}