【Java】K-means算法Java實現以及圖像分割(續)

       胖胖鵬週五也是剛交了作業,現在把之前的代碼更新下。以及po出完整代碼。首先是介紹一下編程的思路。這個比較重要。甚至要高於代碼的重要性。

1.編程思路 

       首先我們要了解,一個像素點,在一幅圖片中存儲需要5個不同的數據,分別是[x,y,r,g,b],這個x和y分別對應着在圖片中的位置,第x行第y列,然後rgb這三個就是電腦顯示的三原色,也就是說,不論什麼顏色,都是由這三個三原色組成顯示的。那麼,知道了這些就好辦了。(胖胖:好辦個鬼哦,5個維度呢)其實xy這兩個維度和聚類無關啦,我們圖像分割主要就是想要保證一個顏色的在一起,所以只要進行RGB的三個維度聚類就口以啦。把每個像素點作爲一個類,這個類包含着RGB三個成員變量,再把圖片讀取成二維數組的形式,就可以保持X和Y不變啦,其實還蠻方便的。之後聚類的算法可以查看我上一篇博客呢。

2.代碼

        廢話不多說,直接上馬。首先是我們爲像素點構造的像素類。
public class dataItem {
	public double r;
	public double g;
	public double b;
	public int group;
}
        四個成員變量,前三個是像素點的RGB值,最後一個group是用來識別該點聚到了第幾類。
        再po出完整代碼:
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;


public class ImageCluster {
	//主要功能就是讀取一副圖像,再對圖像進行分割
	//需要分類的簇數
	private int k;
	//迭代次數
	private int m;
	//數據集合
	private dataItem[][] source;
	//中心集合
	private dataItem[] center;
	//統計每個簇的各項數據的總和,用於計算新的點數
	private dataItem[] centerSum;
	
	//讀取指定目錄的圖片數據,並且寫入數組,這個數據要繼續處理
	private int[][] getImageData(String path){
		BufferedImage bi=null;
		try{
			bi=ImageIO.read(new File(path));
		}catch (IOException e){
			e.printStackTrace();
		}
		int width=bi.getWidth();
		int height=bi.getHeight();
		int [][] data=new int[width][height];
		for(int i=0;i<width;i++)
			for(int j=0;j<height;j++)
				data[i][j]=bi.getRGB(i, j);
		return data;
	}
	//用來處理獲取的像素數據,提取我們需要的寫入dataItem數組
	private dataItem[][] InitData(int [][] data){
		dataItem[][] dataitems=new dataItem[data.length][data[0].length];
		for(int i=0;i<data.length;i++){
			for(int j=0;j<data[0].length;j++){
				dataItem di=new dataItem();
				Color c=new Color(data[i][j]);
				di.r=(double)c.getRed();
				di.g=(double)c.getGreen();
				di.b=(double)c.getBlue();
				di.group=1;
				dataitems[i][j]=di;
			}
		}
		return dataitems;
	}
	//生成隨機的初始中心
	private void initCenters(int k){
		center =new dataItem[k];
		centerSum=new dataItem[k];//用來統計每個聚類裏面的RGB分別之和,方便計算均值
		int width,height;
		for(int i=0;i<k;i++){
			//boolean flag=true;
			dataItem cent=new dataItem();
			dataItem cent2=new dataItem();
		
			width=(int)(Math.random()*source.length);
			height=(int)(Math.random()*source[0].length);
			cent.group=i;
			cent.r=(double)source[width][height].r;
			cent.g=(double)source[width][height].g;
			cent.b=(double)source[width][height].b;
			center[i]=cent;
			

			cent2.r=cent.r;
			cent2.g=cent.g;
			cent2.b=cent.b;
			cent2.group=0;
			centerSum[i]=cent2;
			
			width=0;height=0;
		}
		System.out.println("初始4箇中心");
		for (int i = 0; i < center.length; i++)
		{				
			System.out.println("("+center[i].r+","+center[i].g+","+center[i].b+")");
		}
	}
	//計算兩個像素之間的歐式距離,用RGB作爲三維座標
	private double distance(dataItem first,dataItem second){
		double distance=0;
		distance=Math.sqrt(Math.pow((first.r-second.r),2)+Math.pow((first.g-second.g),2)+
				Math.pow((first.b-second.b),2));
		return distance;
	}
	//返回一個數組中最小的座標
	private int minDistance(double[] distance){
		double minDistance=distance[0];
		int minLocation=0;
		for(int i=0;i<distance.length;i++){
			if(distance[i]<minDistance){
				minDistance=distance[i];
				minLocation=i;
			}else if(distance[i]==minDistance){
				if((Math.random()*10)<5){
					minLocation=i;
				}
			}
		}
		return minLocation;
	}
	//每個點進行分類
	private void clusterSet(){
		int group=-1;
		double distance[]=new double[k];
		for(int i=0;i<source.length;i++){
			for(int j=0;j<source[0].length;j++){
				//求出距離中心點最短的中心
				for(int q=0;q<center.length;q++){
					distance[q]=distance(center[q],source[i][j]);
				}
				group=minDistance(distance);//尋找該點最近的中心
				source[i][j].group=group;//把該點進行分類
				centerSum[group].r+=source[i][j].r;//分類完求出該類的RGB和
				centerSum[group].g+=source[i][j].g;
				centerSum[group].b+=source[i][j].b;
				centerSum[group].group+=1;//這個就是用來統計聚類裏有幾個點
				group=-1;
			}
		}
	}
	//設置新的中心
	public void setNewCenter(){
		for(int i=0;i<centerSum.length;i++){
			System.out.println(i+":"+centerSum[i].group+":"+centerSum[i].r+":"+centerSum[i].g+":"+centerSum[i].b);
                        //取平均值爲新的中心
			center[i].r=(int)(centerSum[i].r/centerSum[i].group);
			center[i].g=(int)(centerSum[i].g/centerSum[i].group);
			center[i].b=(int)(centerSum[i].b/centerSum[i].group);
			//重置之前的求和結果
			centerSum[i].r=center[i].r;
			centerSum[i].g=center[i].g;
			centerSum[i].b=center[i].b;
			centerSum[i].group=0;
		}
	}
	//輸出聚類好的數據
	private void ImagedataOut(String path){
		Color c0=new Color(255,0,0);
		Color c1=new Color(0,255,0);
		Color c2=new Color(0,0,255);
		Color c3=new Color(128,128,128);
		BufferedImage nbi=new BufferedImage(source.length,source[0].length,BufferedImage.TYPE_INT_RGB);
		for(int i=0;i<source.length;i++){
			for(int j=0;j<source[0].length;j++){
				if(source[i][j].group==0)
					nbi.setRGB(i, j, c0.getRGB());
				else if(source[i][j].group==1)
					nbi.setRGB(i, j, c1.getRGB());
				else if(source[i][j].group==2)
					nbi.setRGB(i, j, c2.getRGB());
				else if (source[i][j].group==3)
					nbi.setRGB(i, j, c3.getRGB());
				//Color c=new Color((int)center[source[i][j].group].r,
				//		(int)center[source[i][j].group].g,(int)center[source[i][j].group].b);
				//nbi.setRGB(i, j, c.getRGB());
			}
		}
		try{
			ImageIO.write(nbi, "jpg", new File(path));
		}catch(IOException e){
			e.printStackTrace();
			}
	}
	//進行kmeans計算的核心函數
	public void kmeans(String path,int k,int m){
		source=InitData(getImageData(path));
		/*測試輸出
		for(int i=0;i<source.length;i++)
			for(int j=0;j<source[0].length;j++)
				System.out.println("("+source[i][j].x+","+source[i][j].y+","+source[i][j].r+","+source[i][j].g+","+source[i][j].b+")");
		*/
		this.k=k;
		this.m=m;
		//初始化聚類中心
		initCenters(k);
		/*測試輸出
		for (int i = 0; i < center.length; i++)			
			System.out.println("("+center[i].x+","+center[i].y+","+center[i].r+","+center[i].g+","+center[i].b+")");
		*/
		//進行m次聚類
		for(int level=0;level<m;level++){
			clusterSet();
			setNewCenter();
			for (int i = 0; i < center.length; i++)
			{				
				System.out.println("("+center[i].r+","+center[i].g+","+center[i].b+")");
			}
		}
		clusterSet();
		System.out.println("第"+m+"次迭代完成,聚類中心爲:");
		for (int i = 0; i < center.length; i++)
		{				
			System.out.println("("+center[i].r+","+center[i].g+","+center[i].b+")");
		}
		System.out.println("迭代總次數:"+m);//進行圖像輸出,這個隨意改
		ImagedataOut("E:\\Android\\kmeans\\src\\test4.jpg");
	}

}
     我的代碼註釋寫的很清楚了。。。。我覺得說的再多都不如靜下心來讀代碼。
     墜後附上測試代碼
public class Test {
	
	public static void main(String[] args){
		ImageCluster ic=new ImageCluster();
		ic.kmeans("E:\\Android\\kmeans\\src\\test3.jpg",4,10);
	}
}
 聚類效果嘛。。。還不錯
 我開始用的是Java的代碼,寫了大概200行,後來換用了Python。。。。才20多行。唉。。。下一篇文章我要講下Python的代碼。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章