如何在 opencv 中访问特定的 kmeans 集群
How to access a particular kmeans cluster in opencv
我是 opencv 的新手,我正在尝试查找并保存 kmeaned 聚类图像的最大聚类。我有:
按照Mercury和Bill the Lizard提供的方法将图片聚类如下post(Color classification with k-means in OpenCV),
通过从 kmeans 输出中找到最大的标签数 (bestLables) 来确定最大的集群
试图将构成最大簇的像素的位置存储在一个Point2i数组中
然而,奇怪的是,我发现自己的存储点数明显少于计数
在试图找到最大的簇时获得。换句话说:inc < max。再加上 inc 给出的数字甚至不对应任何其他集群的点数。
我做错了什么?还是有更好的方法来做我想做的事情?任何意见都将不胜感激。
在此先感谢您的宝贵帮助!!
#include <iostream>
#include "opencv2/opencv.hpp"
#include<opencv2/highgui/highgui.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat img = imread("pic.jpg", CV_LOAD_IMAGE_COLOR);
if (!img.data)
{
cout << "Could not open or find the image" << std::endl;
return -1;
}
//imshow("img", img);
Mat imlab;
cvtColor(img, imlab, CV_BGR2Lab);
/* Cluster image */
vector<cv::Mat> imgRGB;
int k = 5;
int n = img.rows *img.cols;
Mat img3xN(n, 3, CV_8U);
split(imlab, imgRGB);
for (int i = 0; i != 3; ++i)
imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));
img3xN.convertTo(img3xN, CV_32F);
Mat bestLables;
kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);
/*bestLables= bestLables.reshape(0,img.rows);
cv::convertScaleAbs(bestLables,bestLables,int(255/k));
cv::imshow("result",bestLables);*/
/* Find the largest cluster*/
int max = 0, indx= 0, id = 0;
int clusters[5];
for (int i = 0; i < bestLables.rows; i++)
{
id = bestLables.at<int>(i, 0);
clusters[id]++;
if (clusters[id] > max)
{
max = clusters[id];
indx = id;
}
}
/* save largest cluster */
int cluster = 1, inc = 0;
Point2i shape[2000];
for (int y = 0; y < imlab.rows; y++)
{
for (int x = 0; x < imlab.cols; x++)
{
if (bestLables.data[y + x*imlab.cols] == cluster) shape[inc++] = { y, x };
}
}
waitKey(0);
return 0;
}
你很接近,但有一些错误。下面的代码应该按预期工作。我还添加了一小段代码来显示分类结果,其中较大簇的像素为红色,另一个为绿色。
- 您从未初始化
int clusters[5];
,因此它将在开头包含随机数,从而危及它作为累加器的作用。我建议改用 vector<int>
。
- 您使用错误的索引访问
bestLabels
。而不是 bestLables.data[y + x*imlab.cols]
,它应该是 bestLables.data[y*imlab.cols + x]
。这导致了您的 inc < max
问题。在下面的代码中,我使用 vector<int>
来包含索引,因为它更容易查看向量的内容。所以我访问 bestLabels
有点不同,即 bestLables[y*imlab.cols + x]
而不是 bestLables.data[y*imlab.cols + x]
,但结果是一样的。
- 你有
Point2i shape[2000];
。我用了一个vector<Point>
。请注意 Point
只是 Point2i
的类型定义。由于您不知道那里会有多少点,因此最好使用动态数组。如果您知道会有 2000 点,您最好调用 reserve
以避免重新分配,但这不是强制性的。使用 Point2i shape[2000];
如果你有超过 2000 分,你将出界,使用 vector
你是安全的。我在附加点时使用 emplace_back
来避免复制(就像您对初始化列表所做的那样)。注意 Point
的构造函数是 (x,y)
,而不是 (y,x)
.
- 使用
vector<Point>
你不需要 inc
,因为你在末尾附加了值。如果需要inc
存储最大簇中的点数,只需调用int inc = shape.size();
- 您已初始化
int cluster = 1
。这是一个错误,您应该使用最大簇的索引对其进行初始化,即 int cluster = indx;
.
- 您正在调用平面向量
imgRGB
,但您正在研究 Lab。您最好更改名称,但这本身不是问题。另外,请记住 RGB 值在 OpenCV 中存储为 BGR,而不是 RGB(倒序)。
- 与
Mat
相比,我更喜欢 Mat1b
、Mat3b
等...。它允许更容易访问并且更具可读性(在我看来)。这不是问题,但您会在我的代码中看到这一点。
我们开始:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int main(int argc, char** argv)
{
Mat3b img = imread("path_to_image");
if (!img.data)
{
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
Mat3b imlab;
cvtColor(img, imlab, CV_BGR2Lab);
/* Cluster image */
vector<cv::Mat3b> imgRGB;
int k = 5;
int n = img.rows * img.cols;
Mat img3xN(n, 3, CV_8U);
split(imlab, imgRGB);
for (int i = 0; i != 3; ++i)
imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));
img3xN.convertTo(img3xN, CV_32F);
vector<int> bestLables;
kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);
/* Find the largest cluster*/
int max = 0, indx= 0, id = 0;
vector<int> clusters(k,0);
for (size_t i = 0; i < bestLables.size(); i++)
{
id = bestLables[i];
clusters[id]++;
if (clusters[id] > max)
{
max = clusters[id];
indx = id;
}
}
/* save largest cluster */
int cluster = indx;
vector<Point> shape;
shape.reserve(2000);
for (int y = 0; y < imlab.rows; y++)
{
for (int x = 0; x < imlab.cols; x++)
{
if (bestLables[y*imlab.cols + x] == cluster)
{
shape.emplace_back(x, y);
}
}
}
int inc = shape.size();
// Show results
Mat3b res(img.size(), Vec3b(0,0,0));
vector<Vec3b> colors;
for(int i=0; i<k; ++i)
{
if(i == indx) {
colors.push_back(Vec3b(0, 0, 255));
} else {
colors.push_back(Vec3b(0, 255 / (i+1), 0));
}
}
for(int r=0; r<img.rows; ++r)
{
for(int c=0; c<img.cols; ++c)
{
res(r,c) = colors[bestLables[r*imlab.cols + c]];
}
}
imshow("Clustering", res);
waitKey(0);
return 0;
}
我是 opencv 的新手,我正在尝试查找并保存 kmeaned 聚类图像的最大聚类。我有:
按照Mercury和Bill the Lizard提供的方法将图片聚类如下post(Color classification with k-means in OpenCV),
通过从 kmeans 输出中找到最大的标签数 (bestLables) 来确定最大的集群
试图将构成最大簇的像素的位置存储在一个Point2i数组中
然而,奇怪的是,我发现自己的存储点数明显少于计数 在试图找到最大的簇时获得。换句话说:inc < max。再加上 inc 给出的数字甚至不对应任何其他集群的点数。
我做错了什么?还是有更好的方法来做我想做的事情?任何意见都将不胜感激。 在此先感谢您的宝贵帮助!!
#include <iostream>
#include "opencv2/opencv.hpp"
#include<opencv2/highgui/highgui.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat img = imread("pic.jpg", CV_LOAD_IMAGE_COLOR);
if (!img.data)
{
cout << "Could not open or find the image" << std::endl;
return -1;
}
//imshow("img", img);
Mat imlab;
cvtColor(img, imlab, CV_BGR2Lab);
/* Cluster image */
vector<cv::Mat> imgRGB;
int k = 5;
int n = img.rows *img.cols;
Mat img3xN(n, 3, CV_8U);
split(imlab, imgRGB);
for (int i = 0; i != 3; ++i)
imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));
img3xN.convertTo(img3xN, CV_32F);
Mat bestLables;
kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);
/*bestLables= bestLables.reshape(0,img.rows);
cv::convertScaleAbs(bestLables,bestLables,int(255/k));
cv::imshow("result",bestLables);*/
/* Find the largest cluster*/
int max = 0, indx= 0, id = 0;
int clusters[5];
for (int i = 0; i < bestLables.rows; i++)
{
id = bestLables.at<int>(i, 0);
clusters[id]++;
if (clusters[id] > max)
{
max = clusters[id];
indx = id;
}
}
/* save largest cluster */
int cluster = 1, inc = 0;
Point2i shape[2000];
for (int y = 0; y < imlab.rows; y++)
{
for (int x = 0; x < imlab.cols; x++)
{
if (bestLables.data[y + x*imlab.cols] == cluster) shape[inc++] = { y, x };
}
}
waitKey(0);
return 0;
}
你很接近,但有一些错误。下面的代码应该按预期工作。我还添加了一小段代码来显示分类结果,其中较大簇的像素为红色,另一个为绿色。
- 您从未初始化
int clusters[5];
,因此它将在开头包含随机数,从而危及它作为累加器的作用。我建议改用vector<int>
。 - 您使用错误的索引访问
bestLabels
。而不是bestLables.data[y + x*imlab.cols]
,它应该是bestLables.data[y*imlab.cols + x]
。这导致了您的inc < max
问题。在下面的代码中,我使用vector<int>
来包含索引,因为它更容易查看向量的内容。所以我访问bestLabels
有点不同,即bestLables[y*imlab.cols + x]
而不是bestLables.data[y*imlab.cols + x]
,但结果是一样的。 - 你有
Point2i shape[2000];
。我用了一个vector<Point>
。请注意Point
只是Point2i
的类型定义。由于您不知道那里会有多少点,因此最好使用动态数组。如果您知道会有 2000 点,您最好调用reserve
以避免重新分配,但这不是强制性的。使用Point2i shape[2000];
如果你有超过 2000 分,你将出界,使用vector
你是安全的。我在附加点时使用emplace_back
来避免复制(就像您对初始化列表所做的那样)。注意Point
的构造函数是(x,y)
,而不是(y,x)
. - 使用
vector<Point>
你不需要inc
,因为你在末尾附加了值。如果需要inc
存储最大簇中的点数,只需调用int inc = shape.size();
- 您已初始化
int cluster = 1
。这是一个错误,您应该使用最大簇的索引对其进行初始化,即int cluster = indx;
. - 您正在调用平面向量
imgRGB
,但您正在研究 Lab。您最好更改名称,但这本身不是问题。另外,请记住 RGB 值在 OpenCV 中存储为 BGR,而不是 RGB(倒序)。 - 与
Mat
相比,我更喜欢Mat1b
、Mat3b
等...。它允许更容易访问并且更具可读性(在我看来)。这不是问题,但您会在我的代码中看到这一点。
我们开始:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int main(int argc, char** argv)
{
Mat3b img = imread("path_to_image");
if (!img.data)
{
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
Mat3b imlab;
cvtColor(img, imlab, CV_BGR2Lab);
/* Cluster image */
vector<cv::Mat3b> imgRGB;
int k = 5;
int n = img.rows * img.cols;
Mat img3xN(n, 3, CV_8U);
split(imlab, imgRGB);
for (int i = 0; i != 3; ++i)
imgRGB[i].reshape(1, n).copyTo(img3xN.col(i));
img3xN.convertTo(img3xN, CV_32F);
vector<int> bestLables;
kmeans(img3xN, k, bestLables, cv::TermCriteria(), 10, cv::KMEANS_RANDOM_CENTERS);
/* Find the largest cluster*/
int max = 0, indx= 0, id = 0;
vector<int> clusters(k,0);
for (size_t i = 0; i < bestLables.size(); i++)
{
id = bestLables[i];
clusters[id]++;
if (clusters[id] > max)
{
max = clusters[id];
indx = id;
}
}
/* save largest cluster */
int cluster = indx;
vector<Point> shape;
shape.reserve(2000);
for (int y = 0; y < imlab.rows; y++)
{
for (int x = 0; x < imlab.cols; x++)
{
if (bestLables[y*imlab.cols + x] == cluster)
{
shape.emplace_back(x, y);
}
}
}
int inc = shape.size();
// Show results
Mat3b res(img.size(), Vec3b(0,0,0));
vector<Vec3b> colors;
for(int i=0; i<k; ++i)
{
if(i == indx) {
colors.push_back(Vec3b(0, 0, 255));
} else {
colors.push_back(Vec3b(0, 255 / (i+1), 0));
}
}
for(int r=0; r<img.rows; ++r)
{
for(int c=0; c<img.cols; ++c)
{
res(r,c) = colors[bestLables[r*imlab.cols + c]];
}
}
imshow("Clustering", res);
waitKey(0);
return 0;
}