简体   繁体   中英

Using OpenCV cv::kmeans() with one-dimensional input

Although the python tutorial uses one-dimensional data, I cannot do the same with the C++ interface:

int size=100;
std::vector<float> data(size);
for (size_t i = 0; i < size ; i++)
{
    data[i] = (float)i; //placeholder
}
std::vector<int> labels;
std::vector<float> centers;
cv::kmeans(data, 3, labels,
    cv::TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 10, 0.1),
    3, cv::KMEANS_PP_CENTERS, centers);

This fails with an internal assertion since cv::kmeans expects the input to be two-dimensional. CV_Assert(N>=K) fails since K is 3 and N is 1. What is my mistake?

The trouble is that when you pass a vector to something that takes an InputArray, when getMat() gets called on that InputArray a Mat with 1 row gets created. But that won't work in this case for the reason Xocoatzin pointed out in the source. You obviously can't reshape a vector, although that has been suggested. If your input is a vector, and you can't change that, you need to explicitly convert the vector into a 1 column Mat, like below.

int size=100;
std::vector<float> data(size);
for (size_t i = 0; i < size ; i++)
{
    data[i] = (float)i; //placeholder
}

cv::Mat data_mat(data.size(), 1, CV32FC1, &data[0]);  // ** Make 1 column Mat from vector

std::vector<int> labels;
std::vector<float> centers;
cv::kmeans(data_mat, 3, labels,     // ** Pass 1 column Mat from Mat to kmeans
   cv::TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 10, 0.1),
    3, cv::KMEANS_PP_CENTERS, centers);

EDIT :

I've just checked the source, it reads:

//...
bool isrow = data.rows == 1 && data.channels() > 1; // MORE THAN ONE CHANNEL
int N = !isrow ? data.rows : data.cols; 
//...

//...
CV_Assert( N >= K );

So, if you have your data in a single row, you need to have more than a single channel in your input matrix and more columns than K .


A quick workaround: reshape your matrix before calling kmeans

It doesn't copy any data, just changes the dimensions of the matrix. So if you have:

[12345678] // mat 1 x 8

After you reshape with 2 rows:

[1234| // a mat 2 x 4
|5678]

You should be able to call kmeans then. (Don't forget to reshape back)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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