基于OpenCV+C++提取图像轮廓后,如何获取图像主色调?
提取图像主色调的OpenCV+C++实现方案
嘿,你已经在做轮廓提取了,现在要搞定主色调提取的话,OpenCV里有几种实用的方法,我结合你的场景给你拆解下~
方法一:基于直方图统计的主色调提取
主色调本质就是图像中出现频率最高的颜色,用直方图统计是最直接的方式。如果是针对特定颜色(比如你提到的蓝色),可以先把图像转到HSV空间(比BGR更适合颜色分析),然后筛选出蓝色范围的像素,统计占比;如果是找整体主色,就统计全图颜色的直方图峰值。
针对特定颜色(比如蓝色)的代码示例
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { Mat image = imread("your_image.jpg"); if (image.empty()) { cout << "无法读取图像!" << endl; return -1; } // 1. 转换到HSV空间 Mat hsv_image; cvtColor(image, hsv_image, COLOR_BGR2HSV); // 2. 定义蓝色的HSV范围(可根据实际需求调整) Scalar lower_blue = Scalar(90, 50, 50); Scalar upper_blue = Scalar(130, 255, 255); // 3. 创建蓝色掩码 Mat blue_mask; inRange(hsv_image, lower_blue, upper_blue, blue_mask); // 4. 统计蓝色像素占比 int total_pixels = image.rows * image.cols; int blue_pixels = countNonZero(blue_mask); float blue_ratio = (float)blue_pixels / total_pixels; cout << "蓝色像素占比:" << blue_ratio * 100 << "%" << endl; // 如果要结合你已有的轮廓:用轮廓生成掩码,和蓝色掩码做与运算 vector<vector<Point>> contours; vector<Vec4i> hierarchy; Mat gray; cvtColor(image, gray, COLOR_BGR2GRAY); findContours(gray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); // 创建轮廓掩码 Mat contour_mask = Mat::zeros(image.size(), CV_8UC1); drawContours(contour_mask, contours, -1, Scalar(255), FILLED); // 只统计轮廓内的蓝色像素 Mat masked_blue; bitwise_and(blue_mask, contour_mask, masked_blue); int contour_blue_pixels = countNonZero(masked_blue); float contour_blue_ratio = (float)contour_blue_pixels / countNonZero(contour_mask); cout << "轮廓内蓝色像素占比:" << contour_blue_ratio * 100 << "%" << endl; return 0; }
方法二:K-Means聚类提取主色调
如果要自动提取图像(或轮廓区域)的Top N主色,K-Means聚类是更灵活的选择。它会把相似颜色聚成簇,占比最大的簇对应的颜色就是主色调。
代码示例(结合轮廓区域)
#include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespace cv; using namespace std; // 获取聚类后占比最大的颜色 Scalar getDominantColor(const Mat& image, const Mat& mask = Mat()) { // 1. 准备样本数据:把图像像素转成一维数组 Mat samples; if (!mask.empty()) { // 只取掩码内的像素 vector<Vec3b> pixels; for (int y = 0; y < image.rows; y++) { for (int x = 0; x < image.cols; x++) { if (mask.at<uchar>(y, x) == 255) { pixels.push_back(image.at<Vec3b>(y, x)); } } } if (pixels.empty()) return Scalar(0,0,0); Mat(pixels).copyTo(samples); } else { image.reshape(1, image.rows * image.cols).copyTo(samples); } samples.convertTo(samples, CV_32F); // 2. K-Means聚类(这里设为3类,可调整) int k = 3; Mat labels, centers; kmeans(samples, k, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0), 3, KMEANS_RANDOM_CENTERS, centers); // 3. 统计每个簇的像素数量 vector<int> cluster_counts(k, 0); for (int i = 0; i < labels.rows; i++) { int label = labels.at<int>(i); cluster_counts[label]++; } // 4. 找到占比最大的簇对应的颜色 int max_count = 0; int dominant_idx = 0; for (int i = 0; i < k; i++) { if (cluster_counts[i] > max_count) { max_count = cluster_counts[i]; dominant_idx = i; } } Scalar dominant_color(centers.at<float>(dominant_idx, 0), centers.at<float>(dominant_idx, 1), centers.at<float>(dominant_idx, 2)); return dominant_color; } int main() { Mat image = imread("your_image.jpg"); if (image.empty()) { cout << "无法读取图像!" << endl; return -1; } // 先提取轮廓(用你已有的代码) vector<vector<Point>> contours; vector<Vec4i> hierarchy; Mat gray; cvtColor(image, gray, COLOR_BGR2GRAY); findContours(gray, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); // 创建轮廓掩码 Mat contour_mask = Mat::zeros(image.size(), CV_8UC1); drawContours(contour_mask, contours, -1, Scalar(255), FILLED); // 获取轮廓区域的主色调 Scalar dominant_color = getDominantColor(image, contour_mask); cout << "轮廓区域主色调(BGR):" << dominant_color << endl; // 如果要获取全图主色,直接传image即可 // Scalar global_dominant = getDominantColor(image); return 0; }
小提示
- HSV的颜色范围可以根据实际场景微调,确保蓝色的筛选更准确;
- K-Means的聚类数
k可以按需调整,k=2-5通常能覆盖图像的主要颜色; - 结合你已有的轮廓,可以精准提取目标区域的主色调,完全避开背景干扰。
内容的提问来源于stack exchange,提问作者user9469525
相关产品推荐
相关产品推荐

