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

时间:2015-07-15 19:27:22

标签: c++ algorithm opencv k-means

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

  • 按照Mercury和Bill the Lizard在以下帖子(Color classification with k-means in OpenCV)中提供的方法对图像进行聚类,

  • 通过查找kmeans输出中最大的标签数来确定最大的群集(bestLables)

  • 尝试在Point2i数组中存储构成最大群集的像素的位置

然而,神秘之处在于我发现自己的存储点数远远少于计数 在尝试找到最大的集群时获得。换句话说:inc<最大。加上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 个答案:

答案 0 :(得分:3)

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

  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的typedef。由于您不知道将有多少点,因此最好使用动态数组。如果您知道会有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的矢量,但您正在研究实验室。您最好更改名称,但这本身并不是问题。另外,请记住,RGB值作为BGR存储在OpenCV中,而不是RGB(反向顺序)。
  7. 我更喜欢Mat1bMat3b等......尽可能超过Mat。它允许更容易访问,并且更具可读性(在我看来)。这不是问题,但您会在我的代码中看到这一点。
  8. 我们走了:

    #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;
    }