如何在 opencv 中访问特定的 kmeans 集群

How to access a particular kmeans cluster in opencv

我是 opencv 的新手,我正在尝试查找并保存 kmeaned 聚类图像的最大聚类。我有:

然而,奇怪的是,我发现自己的存储点数明显少于计数 在试图找到最大的簇时获得。换句话说:inc < max。再加上 inc 给出的数字甚至不对应任何其他集群的点数。

我做错了什么?还是有更好的方法来做我想做的事情?任何意见都将不胜感激。 在此先感谢您的宝贵帮助!!

#include <iostream>
#include "opencv2/opencv.hpp"
#include<opencv2/highgui/highgui.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

using namespace cv;
using namespace std;



int main(int argc, char** argv)
{

    Mat img = imread("pic.jpg", CV_LOAD_IMAGE_COLOR);   

    if (!img.data) 
    {
        cout << "Could not open or find the image" << std::endl;
        return -1;
    }


    //imshow("img", img);

    Mat imlab;
    cvtColor(img, imlab, CV_BGR2Lab);

    /* Cluster image */
    vector<cv::Mat> imgRGB;
    int k = 5;
    int n = img.rows *img.cols;
    Mat img3xN(n, 3, CV_8U);

    split(imlab, imgRGB);

    for (int i = 0; i != 3; ++i)
        imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));

    img3xN.convertTo(img3xN, CV_32F);

    Mat bestLables;
    kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);

    /*bestLables= bestLables.reshape(0,img.rows);
    cv::convertScaleAbs(bestLables,bestLables,int(255/k));
    cv::imshow("result",bestLables);*/

    /* Find the largest cluster*/
    int max = 0, indx= 0, id = 0;
    int clusters[5];

    for (int i = 0; i < bestLables.rows; i++)
    {
        id = bestLables.at<int>(i, 0);
        clusters[id]++;

        if (clusters[id] > max)
        {
            max = clusters[id];
            indx = id;
        }
    }

    /* save largest cluster */
    int cluster = 1, inc = 0;
    Point2i shape[2000]; 

    for (int y = 0; y < imlab.rows; y++)
    {
        for (int x = 0; x < imlab.cols; x++)
        {
            if (bestLables.data[y + x*imlab.cols] == cluster) shape[inc++] = { y, x };
        }
    }


    waitKey(0);


    return 0;
}

你很接近,但有一些错误。下面的代码应该按预期工作。我还添加了一小段代码来显示分类结果,其中较大簇的像素为红色,另一个为绿色。

  1. 您从未初始化 int clusters[5];,因此它将在开头包含随机数,从而危及它作为累加器的作用。我建议改用 vector<int>
  2. 您使用错误的索引访问 bestLabels。而不是 bestLables.data[y + x*imlab.cols],它应该是 bestLables.data[y*imlab.cols + x]。这导致了您的 inc < max 问题。在下面的代码中,我使用 vector<int> 来包含索引,因为它更容易查看向量的内容。所以我访问 bestLabels 有点不同,即 bestLables[y*imlab.cols + x] 而不是 bestLables.data[y*imlab.cols + x],但结果是一样的。
  3. 你有 Point2i shape[2000];。我用了一个vector<Point>。请注意 Point 只是 Point2i 的类型定义。由于您不知道那里会有多少点,因此最好使用动态数组。如果您知道会有 2000 点,您最好调用 reserve 以避免重新分配,但这不是强制性的。使用 Point2i shape[2000]; 如果你有超过 2000 分,你将出界,使用 vector 你是安全的。我在附加点时使用 emplace_back 来避免复制(就像您对初始化列表所做的那样)。注意 Point 的构造函数是 (x,y),而不是 (y,x).
  4. 使用 vector<Point> 你不需要 inc,因为你在末尾附加了值。如果需要inc存储最大簇中的点数,只需调用int inc = shape.size();
  5. 您已初始化 int cluster = 1。这是一个错误,您应该使用最大簇的索引对其进行初始化,即 int cluster = indx;.
  6. 您正在调用平面向量 imgRGB,但您正在研究 Lab。您最好更改名称,但这本身不是问题。另外,请记住 RGB 值在 OpenCV 中存储为 BGR,而不是 RGB(倒序)。
  7. Mat 相比,我更喜欢 Mat1bMat3b 等...。它允许更容易访问并且更具可读性(在我看来)。这不是问题,但您会在我的代码中看到这一点。

我们开始:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;

int main(int argc, char** argv)
{
     Mat3b img = imread("path_to_image");   

    if (!img.data) 
    {
        std::cout << "Could not open or find the image" << std::endl;
        return -1;
    }

    Mat3b imlab;
    cvtColor(img, imlab, CV_BGR2Lab);

    /* Cluster image */
    vector<cv::Mat3b> imgRGB;
    int k = 5;
    int n = img.rows * img.cols;
    Mat img3xN(n, 3, CV_8U);

    split(imlab, imgRGB);

    for (int i = 0; i != 3; ++i)
        imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));

    img3xN.convertTo(img3xN, CV_32F);

    vector<int> bestLables;
    kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);

    /* Find the largest cluster*/
    int max = 0, indx= 0, id = 0;
    vector<int> clusters(k,0);

    for (size_t i = 0; i < bestLables.size(); i++)
    {
        id = bestLables[i];
        clusters[id]++;

        if (clusters[id] > max)
        {
            max = clusters[id];
            indx = id;
        }
    }

    /* save largest cluster */
    int cluster = indx;

    vector<Point> shape; 
    shape.reserve(2000);

    for (int y = 0; y < imlab.rows; y++)
    {
        for (int x = 0; x < imlab.cols; x++)
        {
            if (bestLables[y*imlab.cols + x] == cluster) 
            {
                shape.emplace_back(x, y);
            }
        }
    }

    int inc = shape.size();

    // Show results
    Mat3b res(img.size(), Vec3b(0,0,0));
    vector<Vec3b> colors;
    for(int i=0; i<k; ++i)
    {
        if(i == indx) {
            colors.push_back(Vec3b(0, 0, 255));
        } else {
            colors.push_back(Vec3b(0, 255 / (i+1), 0));
        }
    }

    for(int r=0; r<img.rows; ++r)
    {
        for(int c=0; c<img.cols; ++c)
        {
            res(r,c) = colors[bestLables[r*imlab.cols + c]];
        }
    }

    imshow("Clustering", res);
    waitKey(0);

    return 0;
}