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

2021-12-10 00:00:00 opencv visual-c++ opencv3.0 c++

我想提取与此类似的图像的调色板(来自

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

如何减少原图的颜色数量,如何获取调色板?

解决方案

这里发生了三种不同的事情.

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

减少颜色数量

有很多技巧可以减少颜色的数量.

使用 kmeans 方法,我得到了减少颜色的图像:

它的调色板是:

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

您现在可以在列表中搜索最接近的颜色名称,您将获得所需的内容.如何组成 GUI 来显示这些信息取决于您:数据就在那里.

代码:

#include #include #include #include <地图>使用命名空间 cv;使用命名空间标准;//https://stackoverflow.com/a/34734939/5008845void reduceColor_Quantization(const Mat3b& src, Mat3b& dst){uchar N = 64;dst = src/N;dst *= N;}//https://stackoverflow.com/a/34734939/5008845void reduceColor_kmeans(const Mat3b& src, Mat3b& dst){整数 K = 8;int n = src.rows * src.cols;垫数据 = src.reshape(1, n);data.convertTo(data, CV_32F);向量标签;Mat1f 颜色;kmeans(数据,K,标签,cv::TermCriteria(),1,cv::KMEANS_PP_CENTERS,颜色);for (int i = 0; i (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 减少 = data.reshape(3, src.rows);减少.convertTo(dst, CV_8U);}void reduceColor_Stylization(const Mat3b& src, Mat3b& dst){风格化(src,dst);}void reduceColor_EdgePreserving(const Mat3b& src, Mat3b& dst){edgePreservingFilter(src, dst);}结构体lessVec3b{bool operator()(const Vec3b& lhs, const Vec3b& rhs) const {返回 (lhs[0] != rhs[0]) ?(lhs[0] < rhs[0]) : ((lhs[1] != rhs[1]) ? (lhs[1] < rhs[1]) : (lhs[2] < rhs[2]]));}};mapgetPalette(const Mat3b& src){map调色板;for (int r = 0; r < src.rows; ++r){for (int c = 0; c 调色板 = getPalette(减少);//打印调色板int area = img.rows * img.cols;对于(自动颜色:调色板){cout<<"颜色:"<<颜色.first <<"	 - 区域:"<<100.f * float(color.second)/float(area) <<%"<<结束;}返回0;}

I'd like to extract the color palette of an image, similar to this (from here):

I need it to extract specific colors, like yellow, green, and brown and display the percentage of the area covered by that color. Also, I can add more colors to extract.

How can I reduce the number of colors in the original image, and how can I get the color palette?

解决方案

There are three different things going on here.

  1. Reduce the number of colors of an image
  2. Get the different colors of an image
  3. Get the color name

Reduce the number of colors

There are many techniques to reduce the number of colors. Here you can see how to use color quantization and kmeans.

Another approach could use the median cut algorithm (not shown here).

OpenCV provides the Non-Photorealistic Rendering module. Here you can see some examples of how to use it.

Get the different colors of an image

This is pretty easy. Just iterate over the whole image. If you see a new color, store its value, with counter equal to 1. If you see a color already seen, increment its counter. A std::map could be useful here.

Get the color name

I won't show it here. But online there are some useful resources. You need a list of all named colors. Keep in mind that not every color has a name. In fact, all possible colors for RGB values would be 256*256*256. So find the closest color in your list, and assign its name to your current color.


For example, with this input image,

using kmeans approach, I get the reduced color image:

And its palette is:

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%

You can now search for the closest color name in your list, and you'll get what you need. How to make up the GUI to show these information is up to you: the data is all there.

Code:

#include <opencv2opencv.hpp>
#include <opencv2photo.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 << " 	 - Area: " << 100.f * float(color.second) / float(area) << "%" << endl;
    }

    return 0;
}

相关文章