将 OpenCV cv::kmeans() 与一维输入一起使用

Posted

技术标签:

【中文标题】将 OpenCV cv::kmeans() 与一维输入一起使用【英文标题】:Using OpenCV cv::kmeans() with one-dimensional input 【发布时间】:2014-09-24 12:26:26 【问题描述】:

虽然python tutorial 使用一维数据,但我不能对 C++ 接口做同样的事情:

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);

由于 cv::kmeans 期望输入是二维的,因此内部断言失败。 CV_Assert(N&gt;=K) 失败,因为 K 为 3,N 为 1。我的错误是什么?

【问题讨论】:

什么是“优势”?此外,N 是行向量的输入 Mat 中的元素数。我只是猜测,但你可能弄错了形状。 strengths.reshape(1,1) 应该使它成为一个 n-elem,1 行输入 将其重命名为“数据”。输入是一个 std::vector (通常应该没问题,因为 OpenCV 内部知道如何处理向量作为输入)。但它将其转换为具有一个通道的 (1 x data.size() ) 矩阵。这会导致异常,因为 kmeans 不包含 2 通道输入。 为什么不使用 Mat 数据而不是矢量 断言 (N>=K) 只是确保您拥有至少与您要计算的集群数量一样多的样本。您的问题是,正如@Xocoatzin 在他的回答中所说,将向量传递给 kmeans 而不是 cv::Mat 会导致 N 的计算错误 @"为什么不使用 Mat?":我发现向量更容易理解:当您将鼠标悬停在 IDE 中的对象上时,您可以直接看到存储的内容。矩阵不是清晰类型的。通常,OpenCV 可以处理向量。最后(如答案所示)我使用了一个矩阵。但我相信这是一个 OpenCV 错误。 【参考方案1】:

问题在于,当您将向量传递给采用 InputArray 的对象时,当在该 InputArray 上调用 getMat() 时,会创建一个具有 1 行的 Mat。但由于 Xocoatzin 在源代码中指出的原因,这在这种情况下不起作用。您显然无法重塑矢量,尽管有人建议这样做。如果您的输入是一个向量,并且您无法更改它,您需要将向量显式转换为 1 列 Mat,如下所示。

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);

【讨论】:

最后,两个答案都指向同一个想法 :-) 我现在没有重新塑造我的数据,而是直接将我的数据存储在适当维度的矩阵中,并停止使用向量。【参考方案2】:

编辑

我刚刚检查了来源,上面写着:

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

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

因此,如果您的数据位于一行中,则您的输入矩阵中需要有多个通道和多于K 的列。


快速解决方法:reshape 您的矩阵,然后再调用 kmeans

它不复制任何数据,只是改变矩阵的维度。所以如果你有:

[12345678] // mat 1 x 8

用 2 行重塑后:

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

你应该可以打电话给kmeans。 (别忘了重塑回来)

【讨论】:

我将 fix 推送到 OpenCV 并合并到 3.1 版

以上是关于将 OpenCV cv::kmeans() 与一维输入一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Python,OpenCV中的K均值聚类——K-Means Cluster

K-Means 实现图像分割

OpenCV-Python实战(番外篇)——利用 K-Means 聚类进行色彩量化

一维的opencv dft与matlab fft

在opencv中,如何将二维数组转化为一副图像进行显示?

OpenCV 完整例程72. 一维离散傅里叶变换