K-means 用于聚类,原理很简单,就是将样本按照一定的“距离”,聚类成K个类,聚类过程是一个迭代的过程,即每次都重新计算类中样本的均值作为新的聚类中心。原理很简单,有很多博客写得比较详细,我这里就略过,在此只贴代码和结果。
#include"K-means.h"
/***************************************************************************************************
*function: cluster the rgb image using k-means method
*description:the distance is the sum of absolute difference of the Rgb color component
that is, dis = abs(r0 -r1) + abs(g0 -g1) + abs(b0 -b1), and the initial cluster center
is random;
*param:
*src I 输入彩色图像
*dst O 输出聚类结果
*width I 图像的宽
*height I 图像的高
*clusterNum I 聚类个数
*runTime I 迭代次数
*centerColor I 聚类颜色结构体
****************************************************************************************************/
void TongKmeans(u08 *src, u08 *dst, s32 width, s32 height,
s32 clusterNum, s32 runTime, clusterCenter *centerColor)
{
s32 crossIdx = 0;
clusterCenter center[100] = {0};
s32 idx = 0;
RNG rng;
// inition of the cluster center
for (s32 k = 0; k < clusterNum; k++)
{
center[k].px = rng.uniform(0,width);
center[k].py = rng.uniform(0,height);
s32 idx = 3 * (center[k].py * width +center[k].px);
center[k].color[0] = src[idx];
center[k].color[1] = src[idx + 1];
center[k].color[2] = src[idx + 2];
}
// iteration of clustering
for ( s32 n = 0; n < runTime; n++)
{
// the cluster num should smaller than 300
s32 clusterSum[300][3] = {0};
s32 numCount[300] = {0};
for (s32 i = 0; i < height; i++)
{
crossIdx = 3 * i * width;
for (s32 j = 0; j < width; j ++)
{
s32 minDis = 10000000;
s32 clusterK = 0;
for ( s32 k = 0; k < clusterNum; k++)
{
s32 dis = abs(src[crossIdx] - center[k].color[0])
+ abs(src[crossIdx + 1] - center[k].color[1])
+ abs(src[crossIdx + 2] - center[k].color[2]);
if (dis < minDis)
{
clusterK = k;
minDis = dis;
}
}
// color the pix with certain color
dst[crossIdx] = centerColor[clusterK].color[0];
dst[crossIdx + 1] = centerColor[clusterK].color[1];
dst[crossIdx + 2] = centerColor[clusterK].color[2];
clusterSum[clusterK][0] += src[crossIdx];
clusterSum[clusterK][1] += src[crossIdx + 1];
clusterSum[clusterK][2] += src[crossIdx + 2];
numCount[clusterK]++;
crossIdx += 3;
}
}
for ( s32 k = 0; k < clusterNum; k++)
{
if (numCount[k] == 0)
{
continue;
}
// renew the cluster center
f32 coe = 1.0f / numCount[k];
center[k].color[0] = (u08)(coe * clusterSum[k][0]);
center[k].color[1] = (u08)(coe * clusterSum[k][1]);
center[k].color[2] = (u08)(coe * clusterSum[k][2]);
}
}
}
原图是这样的:
当迭代次数为3, 聚类个数为2时,结果如下:
当迭代次数为10, 分类个数为2时,如下:
可以看出,K-means 在迭代3次或之前就已经收敛了。
那么,改变一下聚类个数,将聚类个数分别设置为10和50,迭代次数依然是3,结果如下:
聚类个数越多,图片就被细分的越厉害,当然,所使用的的聚类标准是比较简单的RGB距离,效果并不是最佳,更多的距离标准可以采用YUV、LAB等其他颜色空间。