2

Before going into deep of my question, I want you to know that I've read other posts on this forum, but none regards my problem. In particular, the post here answers the question "how to do this?" with k-means, while I already know that I have to use it and I'd like to know why my implementation doesn't work.

I want to use k-means algorithm to divide pixels of an input image into clusters, according to their color. Then, after completing such task, I want each pixel to have the color of the center of the cluster it's been assigned to. Taking as reference the OpenCV examples and other stuff retrieved on the web, I've designed the following code:

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>

using namespace std;
using namespace cv;


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

    Mat src = imread( argv[1], 1 );

    // reshape matrix
    Mat resized(src.rows*src.cols, 3, CV_8U);
    int row_counter = 0;

    for(int i = 0; i<src.rows; i++)
    {
        for(int j = 0; j<src.cols; j++)
        {
            Vec3b channels = src.at<Vec3b>(i,j);
            resized.at<char>(row_counter,0) = channels(0);
            resized.at<char>(row_counter,1) = channels(1);
            resized.at<char>(row_counter,2) = channels(2);

            row_counter++;
        }
    }

    //cout << src << endl;

    // change data type
    resized.convertTo(resized, CV_32F);

    // determine termination criteria and number of clusters
    TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, 10, 1.0);
    int K = 8;

    // apply k-means
    Mat labels, centers;
    double compactness = kmeans(resized, K, labels, criteria, 10, KMEANS_RANDOM_CENTERS, centers);

    // change data type in centers
    centers.convertTo(centers, CV_8U);

    // create output matrix
    Mat result = Mat::zeros(src.rows, src.cols, CV_8UC3);

    row_counter = 0;
    int matrix_row_counter = 0;
    while(row_counter < result.rows)
    {
        for(int z = 0; z<result.cols; z++)
        {
            int index = labels.at<char>(row_counter+z, 0);
            //cout << index << endl;
            Vec3b center_channels(centers.at<char>(index,0),centers.at<char>(index,1), centers.at<char>(index,2));
            result.at<Vec3b>(matrix_row_counter, z) = center_channels;
        }

        row_counter += result.cols;
        matrix_row_counter++;
    }

    cout << "Labels " << labels.rows << " " << labels.cols << endl;
    //cvtColor( src, gray, CV_BGR2GRAY );
    //gray.convertTo(gray, CV_32F);

    imshow("Result", result);
    waitKey(0);


    return 0;
}

Anyway, at the end of computation, I simply get a black image. Do you know why? Strangely, if I initialize result matrix as

Mat result(src.size(), src.type())

at the end of algorithm it will display exactly the input image, without any segmentation.

In particular, I have two doubts:

1) is it correct to lay the RGB values of a pixel on each row of matrix resized the way I've done it? is there a way to do it without a loop?

2) what's exactly the content of centers, after k-means function finishes working? it's a 3 columns matrix, does it contains the RGB values of clusters' centers?

thanks for support.

Community
  • 1
  • 1
ubisum
  • 179
  • 3
  • 12
  • Possible duplicate of [Is there a formula to determine overall color given BGR values? (OpenCV and C++)](http://stackoverflow.com/questions/34734379/is-there-a-formula-to-determine-overall-color-given-bgr-values-opencv-and-c) – Miki Sep 21 '16 at 10:41
  • thanks for answer. the post you indicated solved my problem, by giving me a working snippet of code. anyway, I'd like, if possible, to know where I went wrong in mine, since I'm almost sure to make same error next time. Is it possible to have an answer to my two doubts? Thanks for support and sorry if I'm asking too much :) I hope you won't treat this post as the classical case of duplication :) – ubisum Sep 21 '16 at 11:03
  • 1) yes, correct, but they should be float values. You can see in my post how to do it without loops. 2) yes, BGR values of cluster centers in float, you need to convert to uchar. I can't look at this deeper now, but my post should be clear enough – Miki Sep 21 '16 at 17:05

1 Answers1

0

-The below posted OpenCV program assigns the user preferred color to a particular pixel value in an image

-ScanImageAndReduceC() is a predefined method in OpenCV to scan through all the pixels of an Image

-I.atuchar>(10, 10) = 255; is used to access a particular pixel value of an image

Here is the code:

Mat& ScanImageAndReduceC(Mat& I) {

// accept only char type matrices CV_Assert(I.depth() == CV_8U);

int channels = I.channels();

int nRows = I.rows;
int nCols = I.cols * channels;

if (I.isContinuous())
{
    nCols *= nRows;
    nRows = 1;
}

int i, j;
uchar* p;
for (i = 0; i < nRows; ++i)
{
    p = I.ptr<uchar>(i);
    for (j = 0; j < nCols; ++j)
    {
        I.at<uchar>(10, 10) = 255;
    }
}
return I;

}

-------Main Program-------

Calling the above method in our main program

diff = ScanImageAndReduceC(diff);

namedWindow("Difference", WINDOW_AUTOSIZE);// Create a window for display.
imshow("Difference", diff);                   // Show our image inside it.

waitKey(0);                                          // Wait for a keystroke in the window
return 0;

}

DivyaMaheswaran
  • 886
  • 1
  • 12
  • 16