using namespace std;
using namespace cv;
int main()
{
const int MAX_CLUSTERS = 5;
Vec3b colorTab[] =
{
Vec3b(0, 0, 255),
Vec3b(0, 255, 0),
Vec3b(255, 100, 100),
Vec3b(255, 0, 255),
Vec3b(0, 255, 255)
};
Mat data, labels;
Mat pic = imread("C:/Users/dell/Desktop/1.jpg");
for (int i = 0; i < pic.rows; i++)
for (int j = 0; j < pic.cols; j++)
{
Vec3b point = pic.at(i, j);
Mat tmp = (Mat_(1, 3) << point[0], point[1], point[2]);
data.push_back(tmp);
}
//根据浏览图片,确定k=3
kmeans(data, 4, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0),
3, KMEANS_RANDOM_CENTERS);
int n = 0;
//显示聚类结果,不同的类别用不同的颜色显示
for (int i = 0; i < pic.rows; i++)
for (int j = 0; j < pic.cols; j++)
{
int clusterIdx = labels.at(n);
pic.at(i, j) = colorTab[clusterIdx];
n++;
}
imshow("pic", pic);
waitKey(0);
return 0;
}
基本算法步骤:
初始化:从 n个数据对象任意选择 k 个对象作为初始聚类中心;
迭代:
1. 根据每个聚类对象的均值(中心对象),计算每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行分类;
2. 由新的分类数据,重新计算每个(有变化)聚类的均值(中心对象);
3. 计算标准测度函数,当满足一定条件,如函数收敛时,则算法终止;如果条件不满足则循环执行。如opencv中,每次迭代,最大的聚类中心位移max_center_shift < criteria.epsilon小于精度要求时,就结束迭代。以及迭代次数超过设定的最大值时,也结束 iter >= criteria.maxCount。
结束。计算方差,及labels每个对象的分类结果,返回。
算法的时间复杂度上界为O(n*k*t), 其中t是迭代次数。属于非监督学习方法。
OpenCV中增加了参数:
attempts:使用不同的初始化条件,进行分类的次数
flag: KMEANS_RANDOM_CENTERS, KMEANS_PP_CENTERS, KMEANS_USE_INITIAL_LABELS
kmeans算法的2个缺点:第一是必须人为指定所聚的类的个数k;第二是如果使用欧式距离来衡量相似度的话,可能会得到错误的结果,因为没有考虑到属性的重要性和相关性。为了减少这种错误,在使用kmeans距离时,一定要使样本的每一维数据归一化,不然的话由于样本的属性范围不同会导致错误的结果。