首先了解什么是kmeans,它是一种聚类算法,简单理解就是给你一堆数据,让你对他们进行分类,比如网上的例子有给你足球运动员得分等数据信息,让你判断他们在篮球场的位置(前锋、后卫等)。本次的任务是进行图像分割,由于数据是图像二维像素点,彩色图像每个像素点有rgb三个分量,灰度图像只有一个分量。因此每个数据维度是3.
kmeans参考http://blog.csdn.net/hustlx/article/details/50849554
算法:①首先从数据集里随机选取K个值作为初始中心;
②计算每个样本数据到这K个中心的欧式距离(有公式);
③将样本划分到距离最小的中心簇中;
④计算每个簇的均值,作为新的中心;
⑤重复②-④,直到上一次到本次簇中心差值很小时结束。
那么接下来就是按照上述算法进行实现啦
因为是编程小白,肯定有代码重复、变量名乱七八糟的缺点,希望大家能多多指导,不懂得相互交流~~~~
源代码:
import java.awt.List;
import java.util.ArrayList;
import java.util.Random;
/*
首先 就是KMeans算法类,在这里写成了通用类的形式
输入参数:k(最终聚类的数目),sourceData[][](输入的样本数据,其一维长度len_1代表样本数据的数量,二维长度len_2代表每个样本数据所包含的数据个数)
MAX_Its(迭代次数,迭代次数越多应该说明分类越精确吧?)。
输出参数:label[] ,其长度和sourceData相同,存储了每个样本数据的类标记,如label[2]=0,表示第三个样本数据被分配到第1类。
*/
public class KMeans {
//由于这三个数据总是共用不变的因此在这里声明
static int k=0;
static int len_1=0; //源数据数组的长度,即样本数目
static int len_2=0; //源数组二维长度,及每个样本包含数据的数目
double[][] center=new double[k][len_2];//各簇初始中心
public int[] KMeans(int k,double[][] sourceData,int MAX_Its){
KMeans.k=k;
KMeans.len_1= sourceData.length; //源数据数组的长度 也就是多少个像素点
KMeans.len_2=sourceData[0].length; //每个数据的维度 三个像素值 3
int[] label=new int[len_1]; //最终输出,存储了每个样本数据的类标记
center=initcenter(k,sourceData);
for(int i=0;i<MAX_Its;i++){
System.out.println("第 "+i+" 次分类");
label=classify(sourceData,center,k); //分类
center=changecenter(sourceData,label,center,k); //改变中心值
}
for(int i =0;i<label.length;i++)
System.out.println(i+" 被分到第"+label[i]+"类");
return label;
}
//初始化中心值
static public double[][] initcenter(int k,double[][] sourceData){
int[] eachlabel_num=new int[k];
double[][] center=new double[k][];//各簇初始中心
Random random=new Random(System.currentTimeMillis());//生成随机数
//初始化中心值
for(int i=0;i<k;i++){
int ran= random.nextInt(len_1);
System.out.println(i+" random is : "+ran);
center[i]=sourceData[ran];
}
int[] label=classify(sourceData,center,k);
for (int i=0;i<len_1;i++){
for(int j=0;j<k;j++){
if(label[i]==j){
eachlabel_num[j]++;
} else continue;
}
}
int repeat=0;
for(int j=0;j<k;j++){
System.out.println("label "+j+" num is "+eachlabel_num[j]);
if(eachlabel_num[j]==0){
initcenter(k,sourceData);
repeat++;
System.out.println("初始化中心第 "+repeat+" 次");
break;
}else continue;
}
return center;
}
//把每个数据样本分配到距离最小的中心簇
static public int[] classify(double[][] sourceData,double[][] center,int k){
int[] label=new int[len_1];
for(int i=0;i<len_1;i++){
double neardist=Double.MAX_VALUE;//定义最小欧式距离,最初赋予最大值
for(int j=0;j<k;j++){
double dist=calEuraDist(sourceData[i], center[j],len_2); //计算欧式距离
if(dist<neardist){
neardist=dist;
label[i]=j;// 记录每个元素所在分组
}
}
}
return label;
}
//重新计算簇中心
static public double[][] changecenter(double[][] sourceData,int[] label,double[][] center,int k){
int[] eachlabel_num=new int[k];
for (int i=0;i<len_1;i++){
for(int j=0;j<k;j++){
if(label[i]==j){
eachlabel_num[j]++;
} else continue;
}
}
int[][] sum=new int[k][len_2];
for(int i=0;i<label.length;i++){
for(int j=0;j<len_2;j++){
sum[label[i]][j]+=sourceData[i][j];
}
}
for(int i=0;i<k;i++){
for(int j=0;j<len_2;j++){
center[i][j]=sum[i][j]/eachlabel_num[i];
}
}
return center;
}
//计算欧式距离
static public double calEuraDist(double[] vector,double[] center,int len){
double sum=0;
for(int i=0;i<len;i++){
sum+=Math.pow(vector[i]-center[i],2);
}
sum=Math.sqrt(sum);
return sum;
}
}