如何使用opencv从图像中获取调色板?

时间:2016-02-18 10:50:00

标签: c++ opencv visual-c++ opencv3.0

我想提取图像的调色板,类似于此(来自here):

enter image description here

我需要它来提取黄色,绿色和棕色等特定颜色,并显示该颜色所覆盖区域的百分比。另外,我可以添加更多颜色来提取。

如何减少原始图像中的颜色数量,以及如何获得调色板?

1 个答案:

答案 0 :(得分:17)

这里有3种不同的东西。

  1. 减少图像的颜色数
  2. 获取图像的不同颜色
  3. 获取颜色名称
  4. 减少颜色数量

    有许多技术可以减少颜色数量。 Here您可以看到如何使用颜色量化 kmeans

    另一种方法可以使用Median Cut算法(此处未显示)。

    OpenCV提供非真实感渲染 moduleHere您可以看到有关如何使用它的一些示例。

    获取图片的不同颜色

    这很简单。只需迭代整个图像。如果看到新颜色,则存储其值,计数器等于1.如果看到已经看到的颜色,则递增其计数器。 std::map在这里很有用。

    获取颜色名称

    我不会在这里展示它。但在线有some useful resources。您需要一个包含所有命名颜色的列表。请记住,并非每种颜色都有名称。实际上,RGB值的所有可能颜色都是256*256*256。因此,请在列表中找到最接近的颜色,并将其名称指定为当前颜色。

    例如,使用此输入图像:

    enter image description here

    使用 kmeans 方法我得到缩小颜色图像:

    enter image description here

    它的调色板是:

    Color: [14, 134, 225]    - Area: 5.28457%
    Color: [16, 172, 251]    - Area: 27.3851%
    Color: [22, 68, 101]     - Area: 3.41029%
    Color: [28, 154, 161]    - Area: 3.89029%
    Color: [40, 191, 252]    - Area: 22.3429%
    Color: [87, 204, 251]    - Area: 8.704%
    Color: [161, 222, 251]   - Area: 3.47429%
    Color: [253, 255, 255]   - Area: 25.5086%
    

    您现在可以在列表中搜索最接近的颜色名称,并且您将获得所需的颜色。如何构建GUI以显示由您自己决定的这些信息:数据就在那里。

    代码:

    #include <opencv2\opencv.hpp>
    #include <opencv2\photo.hpp>
    #include <iostream>
    #include <map>
    
    using namespace cv;
    using namespace std;
    
    // https://stackoverflow.com/a/34734939/5008845
    void reduceColor_Quantization(const Mat3b& src, Mat3b& dst)
    {
        uchar N = 64;
        dst = src / N;
        dst *= N;
    }
    
    // https://stackoverflow.com/a/34734939/5008845
    void reduceColor_kmeans(const Mat3b& src, Mat3b& dst)
    {
        int K = 8;
        int n = src.rows * src.cols;
        Mat data = src.reshape(1, n);
        data.convertTo(data, CV_32F);
    
        vector<int> labels;
        Mat1f colors;
        kmeans(data, K, labels, cv::TermCriteria(), 1, cv::KMEANS_PP_CENTERS, colors);
    
        for (int i = 0; i < n; ++i)
        {
            data.at<float>(i, 0) = colors(labels[i], 0);
            data.at<float>(i, 1) = colors(labels[i], 1);
            data.at<float>(i, 2) = colors(labels[i], 2);
        }
    
        Mat reduced = data.reshape(3, src.rows);
        reduced.convertTo(dst, CV_8U);
    }
    
    void reduceColor_Stylization(const Mat3b& src, Mat3b& dst)
    {
        stylization(src, dst);
    }
    
    void reduceColor_EdgePreserving(const Mat3b& src, Mat3b& dst)
    {
        edgePreservingFilter(src, dst);
    }
    
    
    struct lessVec3b
    {
        bool operator()(const Vec3b& lhs, const Vec3b& rhs) {
            return (lhs[0] != rhs[0]) ? (lhs[0] < rhs[0]) : ((lhs[1] != rhs[1]) ? (lhs[1] < rhs[1]) : (lhs[2] < rhs[2]));
        }
    };
    
    map<Vec3b, int, lessVec3b> getPalette(const Mat3b& src)
    {
        map<Vec3b, int, lessVec3b> palette;
        for (int r = 0; r < src.rows; ++r)
        {
            for (int c = 0; c < src.cols; ++c)
            {
                Vec3b color = src(r, c);
                if (palette.count(color) == 0)
                {
                    palette[color] = 1;
                }
                else
                {
                    palette[color] = palette[color] + 1;
                }
            }
        }
        return palette;
    }
    
    
    int main()
    {
        Mat3b img = imread("path_to_image");
    
        // Reduce color
        Mat3b reduced;
    
        //reduceColor_Quantization(img, reduced);
        reduceColor_kmeans(img, reduced);
        //reduceColor_Stylization(img, reduced);
        //reduceColor_EdgePreserving(img, reduced);
    
    
        // Get palette
        map<Vec3b, int, lessVec3b> palette = getPalette(reduced);
    
        // Print palette
        int area = img.rows * img.cols;
    
        for (auto color : palette)
        {
            cout << "Color: " << color.first << " \t - Area: " << 100.f * float(color.second) / float(area) << "%" << endl;
        }
    
        return 0;
    }