c++ - 如何使用 OpenCV 从图像中获取调色板

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

<分区>

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

Enter image description here

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

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

最佳答案

这里发生了三件不同的事情。

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

减少颜色数量

有很多技术可以减少颜色的数量。 Here您可以了解如何使用颜色量化kmeans

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

OpenCV 提供了 Non-Photorealistic Rendering module . Here你可以看到一些如何使用它的例子。

获取图像的不同颜色

这很简单。只需遍历整个图像。如果你看到一种新颜色,存储它的值,计数器等于 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) const {
        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;
}

关于c++ - 如何使用 OpenCV 从图像中获取调色板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35479344/

相关文章:

c++ - 如何在 Microsoft Visual Studio 中加载 .xml 文件

python - 安装了 Opencv 但是 python 找不到包

c++ - 类模板成员特化

image-processing - 识别被划掉的手写单词

c++ - 如何为 zlib inflate 函数重新分配输出缓冲区?

c++ - 如何将 C++ 源代码片段放入字符串中?

c++ - 如何从 visual studio 2008 重新分发 vc++ 应用程序

visual-c++ - vc80.pdb - 如何让它消失并且不再打扰我?

c++ - 使用基类方法初始化派生类成员

c++ - C++ 中的 ifstream 对象数组