胖胖鵬週五也是剛交了作業,現在把之前的代碼更新下。以及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是用來識別該點聚到了第幾類。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);
}
}
聚類效果嘛。。。還不錯