#include #include #include #include #include #include #include #include #include #include #include extern "C" { #include } namespace fs = std::filesystem; struct ColorInfo { int r, g, b; double brightness; std::string hex; }; 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); if (image.empty()) { std::cerr << "ошибка: невозможно прочитать картинку" << std::endl; return; } cv::Mat data; image.convertTo(data, CV_32F); data = data.reshape(1, data.total()); cv::Mat labels; cv::Mat centers; cv::TermCriteria criteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0); cv::kmeans(data, num_colors, labels, criteria, 3, cv::KMEANS_PP_CENTERS, centers); std::vector colors; std::vector pastel_colors; for (int i = 0; i < centers.rows; i++) { ColorInfo c; c.b = static_cast(centers.at(i, 0)); c.g = static_cast(centers.at(i, 1)); 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(); colors.push_back(c); cv::Mat bgr_pixel(1, 1, CV_8UC3, cv::Scalar(c.b, c.g, c.r)); cv::Mat hsv_pixel; cv::cvtColor(bgr_pixel, hsv_pixel, cv::COLOR_BGR2HSV); cv::Vec3b& hsv = hsv_pixel.at(0, 0); 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; cv::cvtColor(hsv_pixel, pastel_bgr, cv::COLOR_HSV2BGR); cv::Vec3b pbgr = pastel_bgr.at(0, 0); ColorInfo pc; 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(); pastel_colors.push_back(pc); } std::sort(colors.begin(), colors.end(), [](const ColorInfo& a, const ColorInfo& b) { return a.brightness < b.brightness; }); std::sort(pastel_colors.begin(), pastel_colors.end(), [](const ColorInfo& a, const ColorInfo& b) { return a.brightness < b.brightness; }); ColorInfo bg_color = colors[0]; ColorInfo bg_sub = colors[1]; ColorInfo fg_color = colors[num_colors - 1]; ColorInfo accent_color = colors[1]; double max_distance = 0; for (int i = 1; i < num_colors - 1; i++) { double dist = getColorDistance(colors[i], bg_color); if (dist > max_distance) { max_distance = dist; accent_color = colors[i]; } } ColorInfo fg_sub_color = colors[num_colors - 2]; for (int i = num_colors - 2; i >= 1; i--) { if (colors[i].hex != accent_color.hex) { fg_sub_color = colors[i]; break; } } ColorInfo pas_bg_color = pastel_colors[0]; ColorInfo pas_bg_sub = pastel_colors[1]; ColorInfo pas_fg_color = pastel_colors[num_colors - 1]; ColorInfo pas_accent_color = pastel_colors[1]; double max_dist_pas = 0; for (int i = 1; i < num_colors - 1; i++) { double dist = getColorDistance(pastel_colors[i], pas_bg_color); if (dist > max_dist_pas) { max_dist_pas = dist; pas_accent_color = pastel_colors[i]; } } ColorInfo pas_fg_sub_color = pastel_colors[num_colors - 2]; for (int i = num_colors - 2; i >= 1; i--) { if (pastel_colors[i].hex != pas_accent_color.hex) { pas_fg_sub_color = pastel_colors[i]; break; } } 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; } 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; } 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; } } int download_wall(const char* link, const char* path){ CURL* curl; FILE* fp; CURLcode res; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if(curl) { fp = fopen(path, "wb"); if(fp == NULL) { 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); res = curl_easy_perform(curl); if(res != CURLE_OK) { fprintf(stderr, "ошибка скачивания: %s\n", \ curl_easy_strerror(res)); } fclose(fp); curl_easy_cleanup(curl); } 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; 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_END(), }; struct argparse argparse; 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(); } 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); } } target_dir = fs::path(path_str); } std::string full_path_str(target_dir / "wallpaper.jpg"); const char* full_path = full_path_str.c_str(); download_wall(link, full_path); extract_color_palette(full_path, 6); return 0; }