简体   繁体   English

OpenCV - 如何在灰度图像上应用 Kmeans?

[英]OpenCV - How to apply Kmeans on a grayscale image?

I am trying to cluster a grayscale image using Kmeans.我正在尝试使用 Kmeans 对灰度图像进行聚类。

First, I have a question:首先,我有一个问题:

Is Kmeans the best way to cluster a Mat or are there newer more efficient approaches? Kmeans 是聚类 Mat 的最佳方法还是有更新更有效的方法?

Second, when I try this:其次,当我尝试这个时:

Mat degrees = imread("an image" , IMREAD_GRAYSCALE);
const unsigned int singleLineSize = degrees.rows * degrees.cols;
Mat data = degrees.reshape(1, singleLineSize);
data.convertTo(data, CV_32F);
std::vector<int> labels;
cv::Mat1f colors;
cv::kmeans(data, 3, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.), 2, cv::KMEANS_PP_CENTERS, colors);
for (unsigned int i = 0; i < singleLineSize; i++) {
    data.at<float>(i) = colors(labels[i]);
}

Mat outputImage = data.reshape(1, degrees.rows);
outputImage.convertTo(outputImage, CV_8U);
imshow("outputImage", outputImage);

The result ( outputImage ) is empty.结果 ( outputImage ) 为空。

When I try to multiply colors in the for loop like data.at<float>(i) = 255 * colors(labels[i]);当我尝试在 for 循环中乘以colors ,例如data.at<float>(i) = 255 * colors(labels[i]); I get this error:我收到此错误:

Unhandled exception : Integer division by zero.未处理的异常:整数除以零。

How can I cluster a grayscale image properly?如何正确聚类灰度图像?

It looks to me that you are wrongly parsing the labels and colors info to your output matrix.在我看来,您错误地将标签颜色信息解析为输出矩阵。

K-means returns this info: K-means 返回此信息:

  • Labels - This is an int matrix with all the cluster labels.标签- 这是一个包含所有集群标签的 int 矩阵。 It is a "column" matrix of size TotalImagePixels x 1.它是一个大小为 TotalImagePixels x 1 的“列”矩阵。

  • Centers - This what you refer to as "Colors".中心- 这就是您所说的“颜色”。 This is a float matrix that contains the cluster centers.这是一个包含聚类中心的浮点矩阵。 The matrix is of size NumberOfClusters x featureMean.该矩阵的大小为 NumberOfClusters x featureMean。

In this case, as you are using BGR pixels as "features" consider that Centers has 3 columns: One mean for the B channel, one mean for the G channel and finally, a mean for the R channel.在这种情况下,当您使用BGR 像素作为“特征”时,请考虑中心有 3 列:B 通道的平均值,G 通道的平均值,最后是 R 通道的平均值。

So, basically you loop through the (plain) label matrix, retrieve the label, use this value as index in the Centers matrix to retrieve the 3 colors.所以,基本上你遍历(普通)标签矩阵,检索标签,使用这个值作为中心矩阵中的索引来检索 3 种颜色。

One way to do this is as follows, using the auto data specifier and looping through the input image instead (that way we can index each input label easier):一种方法如下,使用自动数据说明符并循环输入图像(这样我们可以更容易地索引每个输入标签):

    //prepare an empty output matrix
    cv::Mat outputImage( inputImage.size(), inputImage.type() );

    //loop thru the input image rows...
    for( int row = 0; row != inputImage.rows; ++row ){

        //obtain a pointer to the beginning of the row
        //alt: uchar* outputImageBegin = outputImage.ptr<uchar>(row);            
        auto outputImageBegin = outputImage.ptr<uchar>(row);

        //obtain a pointer to the end of the row
        auto outputImageEnd = outputImageBegin + outputImage.cols * 3;

        //obtain a pointer to the label:
        auto labels_ptr = labels.ptr<int>(row * inputImage.cols);

        //while the end of the image hasn't been reached...
        while( outputImageBegin != outputImageEnd ){

            //current label index:
            int const cluster_idx = *labels_ptr;

            //get the center of that index:
            auto centers_ptr = centers.ptr<float>(cluster_idx);

            //we got an implicit VEC3B vector, we must map the BGR items to the
            //output mat:
            clusteredImageBegin[0] = centers_ptr[0];
            clusteredImageBegin[1] = centers_ptr[1];
            clusteredImageBegin[2] = centers_ptr[2];

            //increase the row "iterator" of our matrices:
            clusteredImageBegin += 3; ++labels_ptr;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM