k-means算法又名k均值聚类算法。其算法思想大致为:先从样本集中随机选取 k 个样本作为类别中心,并计算所有样本与这 k 个类别中心的距离,对于每一个样本,将其划分到与其距离最近的类别中心所在的类别中,对于新的类别计算各个类别的新的类别中心。
根据以上描述,我们大致可以猜测到实现k-means算法的主要三点:
(1)类别个数 k 的选择
(2)各个样本点到类别中心的距离
(3)根据新划分的类别,更新类别中心
代码实现
首先定义类别中心:
struct KMeansClassCenter
{
vector <int> classcenter;//类别中心,因为是1个像元,故不采用float或double
vector <int> pixCode;//记录该类别的像元号
};
再定义每个点的象元值的属性
struct KMeansPixData
{
vector <int> pixs; //像元值
KMeansClassCenter classcenter;//类别中心
double class_distance;//距离
int classNum;//类别号
};
进行聚类划分
bool kmeans(KMeansPixData *pixdata, int nBandCount, int nImgSizeX, int nImgSizeY, int m_maxClassNum, int m_maxIteratNum, int * outputpafScan)
{
KMeansClassCenter *classCenter;
pixdata = new KMeansPixData[nImgSizeX * nImgSizeY];
for (int i = 0; i < m_maxClassNum; ++i)//给类别中心赋初始值
{
int classnum = (i + 0.5) * nImgSizeX * nImgSizeY / m_maxClassNum;
classCenter[i].classcenter = pixdata[classnum].pixs;
}
for (int IteratNum = 0; IteratNum < m_maxIteratNum; ++IteratNum)//迭代
{
for(int pixcount = 0; pixcount < nImgSizeX * nImgSizeY; ++pixcount)//遍历像元
{
for (int classnum = 0; classnum < m_maxClassNum; ++classnum)//计算与每个类别中心的距离
{
float perclassdistance = 0;
for (int bandcount = 0; bandcount < nBandCount; ++bandcount)
{
perclassdistance += (pixdata[pixcount].pixs[bandcount] - classCenter[classnum].classcenter[bandcount])
* (pixdata[pixcount].pixs[bandcount] - classCenter[classnum].classcenter[bandcount]);
}
perclassdistance = sqrt(perclassdistance);
if (classnum == 0)
pixdata[pixcount].class_distance = sqrt((float)nImgSizeX * nImgSizeX + nImgSizeY * nImgSizeY);
if (pixdata[pixcount].class_distance > perclassdistance)
{
pixdata[pixcount].class_distance = perclassdistance;
pixdata[pixcount].classNum = classnum;
}
classCenter[pixdata[pixcount].classNum].pixCode.push_back(pixcount);//记录每个类别包括的像元号
}
}
for (int classnum = 0; classnum < m_maxClassNum; ++classnum)//计算类别中心
{
for(int bandcount = 0; bandcount < nBandCount; ++bandcount)
{
int sum = 0;
int maxsize = classCenter[classnum].pixCode.size();
for (int classSize = 0; classSize < maxsize; ++classSize)//该类别像元个数
sum += pixdata[classCenter[classnum].pixCode[classSize]].pixs[bandcount];
if (maxsize == 0) continue;
sum /= maxsize;
classCenter[classnum].classcenter[bandcount] = (int)sum;
}
}
}
for(int bandcount = 0; bandcount < nBandCount; ++bandcount)
{
for(int pixcount = 0; pixcount < nImgSizeX * nImgSizeY; ++pixcount)
outputpafScan[pixcount] = pixdata[pixcount].classNum;
}
return true;
}
原始图像:
k-means聚类的结果:
欢迎大家批评指正。