package fr.unistra.pelican.algorithms.segmentation;
import java.util.Arrays;
import fr.unistra.pelican.Algorithm;
import fr.unistra.pelican.AlgorithmException;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.IntegerImage;
/**
* This algorithm performs a classic K-Means
*
* Works in ND and with any number of bands
*
* @author Jonathan Weber
*
*/
public class KMeans extends Algorithm {
/**
* Input Image
*/
public Image inputImage;
/**
* Number of clusters
*/
public int k;
/**
* Number max of iterations
*/
public int maxIter=Integer.MAX_VALUE;
/**
* Label image
*/
public IntegerImage outputImage;
private int[][] centroids;
private int[] clusterSize;
private IntegerImage currentMap;
private int bDim;
private int outputSize;
public KMeans()
{
super.inputs="inputImage,k";
super.options="maxIter";
super.outputs="outputImage";
}
@Override
public void launch() throws AlgorithmException
{
bDim = inputImage.getBDim();
currentMap = inputImage.newIntegerImage(inputImage.getXDim(), inputImage.getYDim(), inputImage.getZDim(), inputImage.getTDim(), 1);
outputSize = currentMap.size();
centroids = new int[k][bDim];
clusterSize = new int[k];
int iterations=0;
//Initialize cluster
for(int currentk=0;currentk<k;currentk++)
{
int pixel = ((int)(Math.random()*outputSize))*bDim;
for(int b=0;b<bDim;b++)
{
centroids[currentk][b] = inputImage.getPixelByte(pixel+b);
}
}
affectPixelsToCluster();
while(!outputImage.equals(currentMap)&&iterations<maxIter)
{
computeCentroids();
affectPixelsToCluster();
iterations++;
}
}
public void affectPixelsToCluster()
{
outputImage = currentMap;
currentMap = outputImage.newIntegerImage();
double dMin;
int[] currentValue = new int[3];
int currentAssignedCluster=-1;
for(int i=0;i<outputSize;i++)
{
dMin = Double.MAX_VALUE;
int pixelLoc = i*bDim;
for(int b=0;b<bDim;b++)
{
currentValue[b]=inputImage.getPixelByte(pixelLoc);
pixelLoc++;
}
for(int currentk=0;currentk<k;currentk++)
{
double distance=0;
for(int b=0;b<bDim;b++)
{
double attributeDistance = currentValue[b]-centroids[currentk][b];
distance+= attributeDistance*attributeDistance;
}
if(distance<dMin)
{
dMin=distance;
currentAssignedCluster=currentk;
}
}
currentMap.setPixelInt(i,currentAssignedCluster);
}
}
public void computeCentroids()
{
for(int currentk=0;currentk<k;currentk++)
Arrays.fill(centroids[currentk], 0);
Arrays.fill(clusterSize,0);
for(int i=0;i<outputSize;i++)
{
int currentk = currentMap.getPixelInt(i);
clusterSize[currentk]++;
int pixelLoc = i*bDim;
for(int b=0;b<bDim;b++)
{
centroids[currentk][b]+=inputImage.getPixelByte(pixelLoc);
pixelLoc++;
}
}
for(int currentk=0;currentk<k;currentk++)
for(int b=0;b<bDim;b++)
{
if(clusterSize[currentk]!=0)
centroids[currentk][b]/=clusterSize[currentk];
}
}
/**
* Performs a K-Means
*
* @param inputImage
* @param k
* @return label image
*/
public static IntegerImage exec (Image inputImage,int k)
{
return (IntegerImage) new KMeans().process(inputImage,k);
}
/**
* Performs a K-Means
*
* @param inputImage
* @param k
* @param maxIter
* @return label image
*/
public static IntegerImage exec (Image inputImage,int k,int maxIter)
{
return (IntegerImage) new KMeans().process(inputImage,k,maxIter);
}
}