From 7531515e7674e1f8e1bb7e2c8ee49c873fb29f1a Mon Sep 17 00:00:00 2001 From: s13bby Date: Sat, 5 Sep 2026 18:28:32 +0600 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20+=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE:=20=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D1=8F=D1=82=D0=BD=D1=83=D1=82=D1=8C=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB.=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82=20=D0=B0=D1=80=D0=B3?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=20=D1=81=D0=BE=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=B0=D0=BB=D0=B5=D1=82?= =?UTF-8?q?=D1=82=20=D0=BF=D0=BE=20=D0=BF=D1=83=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 210 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 115 insertions(+), 95 deletions(-) diff --git a/main.cpp b/main.cpp index c7fe215..e2c1dfd 100644 --- a/main.cpp +++ b/main.cpp @@ -7,10 +7,12 @@ #include #include #include +#include #include #include + extern "C" { - #include + #include "libs/argparse.h" } namespace fs = std::filesystem; @@ -25,21 +27,27 @@ double getColorDistance(const ColorInfo& c1, const ColorInfo& c2) { return std::sqrt(std::pow(c1.r - c2.r, 2) + std::pow(c1.g - c2.g, 2) + std::pow(c1.b - c2.b, 2)); } -void extract_color_palette(const std::string& image_path, int num_colors = 6) { - cv::Mat image = cv::imread(image_path); +void extract_color_palette(const std::string& image_path, int num_colors, const std::string& output_path) { + if (num_colors < 3) { + std::cerr << "ошибка: для генерации палитры нужно минимум 3 цвета" << std::endl; + return; + } + cv::Mat image = cv::imread(image_path); if (image.empty()) { - std::cerr << "ошибка: невозможно прочитать картинку" << std::endl; + std::cerr << "ошибка: невозможно прочитать картинку по пути " << image_path << std::endl; return; } cv::Mat data; image.convertTo(data, CV_32F); - data = data.reshape(1, data.total()); + + data = data.reshape(3, data.total()); cv::Mat labels; cv::Mat centers; - cv::TermCriteria criteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0); + cv::TermCriteria criteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 100, 1.0); + cv::kmeans(data, num_colors, labels, criteria, 3, cv::KMEANS_PP_CENTERS, centers); std::vector colors; @@ -52,11 +60,9 @@ void extract_color_palette(const std::string& image_path, int num_colors = 6) { c.r = static_cast(centers.at(i, 2)); c.brightness = 0.299 * c.r + 0.587 * c.g + 0.114 * c.b; - std::stringstream ss; - ss << "#" << std::setfill('0') << std::setw(2) << std::hex << c.r - << std::setfill('0') << std::setw(2) << std::hex << c.g - << std::setfill('0') << std::setw(2) << std::hex << c.b; - c.hex = ss.str(); + char hex_str[8]; + snprintf(hex_str, sizeof(hex_str), "#%02x%02x%02x", c.r, c.g, c.b); + c.hex = hex_str; colors.push_back(c); cv::Mat bgr_pixel(1, 1, CV_8UC3, cv::Scalar(c.b, c.g, c.r)); @@ -67,7 +73,6 @@ void extract_color_palette(const std::string& image_path, int num_colors = 6) { hsv[1] = std::min(static_cast(hsv[1]), 60); if (hsv[1] < 30) hsv[1] = 40; - hsv[2] = std::max(static_cast(hsv[2]), 230); cv::Mat pastel_bgr; @@ -78,11 +83,9 @@ void extract_color_palette(const std::string& image_path, int num_colors = 6) { pc.b = pbgr[0]; pc.g = pbgr[1]; pc.r = pbgr[2]; pc.brightness = 0.299 * pc.r + 0.587 * pc.g + 0.114 * pc.b; - std::stringstream ss_pastel; - ss_pastel << "#" << std::setfill('0') << std::setw(2) << std::hex << pc.r - << std::setfill('0') << std::setw(2) << std::hex << pc.g - << std::setfill('0') << std::setw(2) << std::hex << pc.b; - pc.hex = ss_pastel.str(); + char hex_str_pas[8]; + snprintf(hex_str_pas, sizeof(hex_str_pas), "#%02x%02x%02x", pc.r, pc.g, pc.b); + pc.hex = hex_str_pas; pastel_colors.push_back(pc); } @@ -137,53 +140,28 @@ void extract_color_palette(const std::string& image_path, int num_colors = 6) { } } - std::ofstream file_dark("palette_dark"); - if (file_dark.is_open()) { - file_dark << bg_color.hex << "\n" - << bg_sub.hex << "\n" - << accent_color.hex << "\n" - << fg_sub_color.hex << "\n" - << fg_color.hex << "\n"; - file_dark.close(); - std::cout << "успешно сформирована темная палитра" << std::endl; - } + fs::path out_dir(output_path); - std::ofstream file_light("palette_light"); - if (file_light.is_open()) { - file_light << fg_color.hex << "\n" - << fg_sub_color.hex << "\n" - << accent_color.hex << "\n" - << bg_sub.hex << "\n" - << bg_color.hex << "\n"; - file_light.close(); - std::cout << "успешно сформирована светлая палитра" << std::endl; - } + auto write_palette = [&](const std::string& filename, const std::vector& palette) { + std::ofstream file(out_dir / filename); + if (file.is_open()) { + for (const auto& color : palette) { + file << color.hex << "\n"; + } + file.close(); + std::cout << "успешно сформирована палитра: " << filename << std::endl; + } else { + std::cerr << "ошибка: не удалось создать файл " << filename << std::endl; + } + }; - std::ofstream file_pastel_dark("palette_pastel_dark"); - if (file_pastel_dark.is_open()) { - file_pastel_dark << pas_bg_color.hex << "\n" - << pas_bg_sub.hex << "\n" - << pas_accent_color.hex << "\n" - << pas_fg_sub_color.hex << "\n" - << pas_fg_color.hex << "\n"; - file_pastel_dark.close(); - std::cout << "успешно сформирована пастельная палитра" << std::endl; - } - - std::ofstream file_pastel_link("palette_pastel_light"); - if (file_pastel_link.is_open()) { - file_pastel_link << pas_fg_color.hex << "\n" - << pas_fg_sub_color.hex << "\n" - << pas_accent_color.hex << "\n" - << pas_bg_sub.hex << "\n" - << pas_bg_color.hex << "\n"; - file_pastel_link.close(); - std::cout << "успешно сформирована пастельная палитра" << std::endl; - } + write_palette("palette_dark", {bg_color, bg_sub, accent_color, fg_sub_color, fg_color}); + write_palette("palette_light", {fg_color, fg_sub_color, accent_color, bg_sub, bg_color}); + write_palette("palette_pastel_dark", {pas_bg_color, pas_bg_sub, pas_accent_color, pas_fg_sub_color, pas_fg_color}); + write_palette("palette_pastel_light", {pas_fg_color, pas_fg_sub_color, pas_accent_color, pas_bg_sub, pas_bg_color}); } - -int download_wall(const char* link, const char* path){ +int download_wall(const char* link, const char* path) { CURL* curl; FILE* fp; CURLcode res; @@ -193,52 +171,50 @@ int download_wall(const char* link, const char* path){ if(curl) { fp = fopen(path, "wb"); - if(fp == NULL) { - fprintf(stderr, \ - "ошибка: не удалось создать или открыть файл %s\n", path); - + fprintf(stderr, "ошибка: не удалось создать или открыть файл %s\n", path); curl_easy_cleanup(curl); curl_global_cleanup(); - return 1; } curl_easy_setopt(curl, CURLOPT_URL, link); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) WallpaperPaletteBot/1.0"); res = curl_easy_perform(curl); - if(res != CURLE_OK) { - fprintf(stderr, "ошибка скачивания: %s\n", \ - curl_easy_strerror(res)); - } - fclose(fp); - curl_easy_cleanup(curl); + + if(res != CURLE_OK) { + fprintf(stderr, "ошибка скачивания: %s\n", curl_easy_strerror(res)); + curl_global_cleanup(); + return 1; + } + } else { + curl_global_cleanup(); + return 1; } curl_global_cleanup(); - return 0; } int main(int argc, const char **argv) { - const char* link = nullptr; const char* raw_scheme_path = nullptr; const char* raw_wall_path = nullptr; + const char* raw_path_to_local = nullptr; struct argparse_option options[] = { OPT_HELP(), - OPT_STRING(0,"link", &link, \ - "ссылка на изображение с сайта wallhave.cc", NULL, 0, 0), - OPT_STRING(0,"scheme-output", &raw_scheme_path,\ - "директория для сохранения палитр", NULL, 0, 0), - OPT_STRING(0,"wall-output", &raw_wall_path, \ - "директория для сохранения обоев", NULL, 0, 0), + OPT_STRING(0, "link", &link, "ссылка на изображение с сайта wallhave.cc", NULL, 0, 0), + OPT_STRING(0, "local", &raw_path_to_local, "путь до локального файла с обоями", NULL, 0, 0), + OPT_STRING(0, "scheme-output", &raw_scheme_path, "директория для сохранения палитр", NULL, 0, 0), + OPT_STRING(0, "wall-output", &raw_wall_path, "директория для сохранения обоев", NULL, 0, 0), OPT_END(), }; @@ -246,27 +222,71 @@ int main(int argc, const char **argv) { argparse_init(&argparse, options, nullptr, 0); argc = argparse_parse(&argparse, argc, argv); - fs::path target_dir; - - if (raw_wall_path == nullptr || std::string(raw_wall_path).empty()){ - target_dir = fs::current_path(); + if (link == nullptr && raw_path_to_local == nullptr) { + std::cerr << "ошибка: необходимо указать либо --link, либо --local" << std::endl; + return 1; } - else{ - std::string path_str(raw_wall_path); - if (path_str[0] == '~'){ - const char* user_home = std::getenv("HOME"); - if (user_home){ - path_str.replace(0, 1, user_home); - } + if (link != nullptr && raw_path_to_local != nullptr) { + std::cerr << "ошибка: нельзя указать одновременно --link и --local. Выберите что-то одно." << std::endl; + return 1; + } + + auto resolve_dir = [](const char* raw_path) -> fs::path { + if (raw_path == nullptr || std::string(raw_path).empty()) { + return fs::current_path(); } - target_dir = fs::path(path_str); + std::string path_str(raw_path); + if (path_str[0] == '~') { + const char* user_home = std::getenv("HOME"); + if (user_home) path_str.replace(0, 1, user_home); + } + return fs::path(path_str); + }; + + fs::path target_dir_wall = resolve_dir(raw_wall_path); + if (!fs::exists(target_dir_wall)) { + fs::create_directories(target_dir_wall); } - std::string full_path_str(target_dir / "wallpaper.jpg"); - const char* full_path = full_path_str.c_str(); + fs::path target_dir_scheme = resolve_dir(raw_scheme_path); + if (!fs::exists(target_dir_scheme)) { + fs::create_directories(target_dir_scheme); + } - download_wall(link, full_path); - extract_color_palette(full_path, 6); + std::string final_image_path; + + if (raw_path_to_local != nullptr) { + std::string local_str(raw_path_to_local); + + if (local_str[0] == '~') { + const char* user_home = std::getenv("HOME"); + if (user_home) local_str.replace(0, 1, user_home); + } + + fs::path local_file(local_str); + + if (!fs::exists(local_file)) { + std::cerr << "ошибка: локальный файл не найден по пути " << local_file << std::endl; + return 1; + } + + final_image_path = local_file.string(); + std::cout << "используется локальный файл: " << final_image_path << std::endl; + + } else { + std::string full_wall_path = (target_dir_wall / "wallpaper.jpg").string(); + + std::cout << "скачивание изображения..." << std::endl; + if (download_wall(link, full_wall_path.c_str()) != 0) { + std::cerr << "не удалось скачать изображение. завершение работы." << std::endl; + return 1; + } + + final_image_path = full_wall_path; + } + + std::cout << "извлечение палитры..." << std::endl; + extract_color_palette(final_image_path, 6, target_dir_scheme.string()); return 0; } \ No newline at end of file