From 4d459313db9b27b41a581696984fdf02e7f439d0 Mon Sep 17 00:00:00 2001 From: s13bby Date: Sat, 5 Sep 2026 01:14:27 +0600 Subject: [PATCH] =?UTF-8?q?=D1=83=D1=82=D0=B8=D0=BB=D0=B8=D1=82=D0=B0=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D0=BE=D0=BD=D1=87=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- CMakeLists.txt | 7 +- build.sh | 23 +++++-- main.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 199 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index e708181..2714028 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,5 @@ dkms.conf zenith-cli *.jpg -build/ \ No newline at end of file +build/ +palette* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index cc3ab5a..1d5d403 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,12 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(CURL REQUIRED) +find_package(OpenCV REQUIRED) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs) +include_directories( + ${OpenCV_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/libs +) add_executable(zenith-cli main.cpp @@ -16,4 +20,5 @@ add_executable(zenith-cli target_link_libraries(zenith-cli PRIVATE CURL::libcurl + ${OpenCV_LIBS} ) diff --git a/build.sh b/build.sh index 25725cb..2868df0 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,21 @@ -#!/bin/bash -echo "начинаю сборку" +#!/usr/bin/env bash -g++ main.cpp libs/argparse.c -o zenith-cli -lcurl +set -e -echo "сборкa завершена" \ No newline at end of file +BUILD_DIR="build" +OUTPUT="../zenith-cli" + +echo "==> очистка директории build..." +rm -rf "$BUILD_DIR" + +echo "==> конфигурирование CMake..." +cmake -S . -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release + +echo "==> сборка..." +cmake --build "$BUILD_DIR" --config Release -j"$(nproc 2>/dev/null || sysctl -n hw.ncpu)" + +echo "==> копирование исполняемого файла..." +cp "$BUILD_DIR/zenith-cli" "$OUTPUT" + +echo "==> сборка завершена!" +echo "Executable: $OUTPUT" \ No newline at end of file diff --git a/main.cpp b/main.cpp index bf52a56..d615337 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include + #include "curl/curl.h" @@ -11,21 +18,174 @@ extern "C" { namespace fs = std::filesystem; -struct Scheme{ - char fg_color[8]; - char sub_fg_color[8]; - - char bg_color[8]; - char sub_bg_color[8]; - - char accent[8]; - char sub_accent[8]; +struct ColorInfo { + int r, g, b; + double brightness; + std::string hex; }; -void write_pallete(const struct Scheme *s){ -//s->bg_color +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; @@ -113,6 +273,7 @@ int main(int argc, const char **argv) { const char* full_path = full_path_str.c_str(); download_wall(link, full_path); + extract_color_palette(full_path, 6); return 0; } \ No newline at end of file