OpenCV - 如何在灰度图像上应用 Kmeans?
OpenCV - How to apply Kmeans on a grayscale image?
我正在尝试使用 Kmeans 对灰度图像进行聚类。
首先,我有一个问题:
Kmeans 是对 Mat 进行聚类的最佳方法还是有更新的更有效的方法?
其次,当我尝试这个时:
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);
结果(outputImage
)为空。
当我尝试在 for 循环中乘以 colors
时,例如 data.at<float>(i) = 255 * colors(labels[i]);
我收到此错误:
Unhandled exception : Integer division by zero.
如何正确聚类灰度图像?
在我看来,您错误地将 labels 和 colors 信息解析为输出矩阵。
K-means returns 这个信息:
Labels - 这是一个包含所有簇标签的 int 矩阵。它是一个大小为 TotalImagePixels x 1 的 "column" 矩阵。
中心 - 这就是您所说的 "Colors"。这是一个包含聚类中心的浮点矩阵。矩阵的大小
NumberOfClusters x featureMean.
在这种情况下,当您使用 BGR 像素 作为 "features" 时,请考虑中心有 3 列:B 通道的一个平均值,G 通道的一个平均值通道,最后是 R 通道的平均值。
所以,基本上你遍历(普通)标签矩阵,检索标签,使用这个值作为中心矩阵中的索引来检索 3 种颜色。
一种方法如下,使用自动数据说明符并循环遍历输入图像(这样我们可以更容易地索引每个输入标签):
//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;
}
}
我正在尝试使用 Kmeans 对灰度图像进行聚类。
首先,我有一个问题:
Kmeans 是对 Mat 进行聚类的最佳方法还是有更新的更有效的方法?
其次,当我尝试这个时:
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);
结果(outputImage
)为空。
当我尝试在 for 循环中乘以 colors
时,例如 data.at<float>(i) = 255 * colors(labels[i]);
我收到此错误:
Unhandled exception : Integer division by zero.
如何正确聚类灰度图像?
在我看来,您错误地将 labels 和 colors 信息解析为输出矩阵。
K-means returns 这个信息:
Labels - 这是一个包含所有簇标签的 int 矩阵。它是一个大小为 TotalImagePixels x 1 的 "column" 矩阵。
中心 - 这就是您所说的 "Colors"。这是一个包含聚类中心的浮点矩阵。矩阵的大小 NumberOfClusters x featureMean.
在这种情况下,当您使用 BGR 像素 作为 "features" 时,请考虑中心有 3 列:B 通道的一个平均值,G 通道的一个平均值通道,最后是 R 通道的平均值。
所以,基本上你遍历(普通)标签矩阵,检索标签,使用这个值作为中心矩阵中的索引来检索 3 种颜色。
一种方法如下,使用自动数据说明符并循环遍历输入图像(这样我们可以更容易地索引每个输入标签):
//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;
}
}