javaimage-processingk-meansimage-segmentationopenimaj

How to set the initial centroids of KMColourSegmenter in OpenIMAJ library?


I want to measure the time that KMColourSegmenter, in OpenIMAJ library, takes to perform the clustering.

If I didn't make the initial centroids fixed, rather than random, I can't make the measure the performance; because it will change every time, give different number of iterations , and vary in time to execute the clustering.

So how to make the initial centroids fixed i.e. setting them manually?

Update:

@Jon Thanks for the answer, I am trying to implement what you said. Could you check it, especially the "clusters" array I think this array doesn't make sense to initialize. Please, correct me if I am wrong.

public class MyFloatKMeansInit extends FloatKMeansInit{

    @Override
    public void initKMeans(DataSource<float[]> bds, float[][] clusters) throws IOException {
        // TODO Auto-generated method stub
        for (int i = 0; i < bds.size(); i++) {
            for (int j = 0; j < bds.getData(i).length; j++) {
                clusters[i][j]=bds.getData(i)[j];
            }
        }   
    }
}

public class MyKMColourSegmenter extends KMColourSegmenter{

    public MyKMColourSegmenter(FloatArrayBackedDataSource bds, ColourSpace colourSpace, int K) throws IOException {
        super(colourSpace, K);
        MyFloatKMeansInit myFloatKMeansInit = new MyFloatKMeansInit();
        float[][] clusters = new float[K][];//#######I think there is something wrong here
        myFloatKMeansInit.initKMeans(bds, clusters);
        this.kmeans.setInit(myFloatKMeansInit);
        // TODO Auto-generated constructor stub
    }

}

Solution

  • You'll have to implement this yourself; create a subclass of KMColourSegmenter and create a constructor that takes in the centroids, as well as any parameters needed by your choice of constructor in the KMColourSegmenter super class. Then in your constructor, after the call to super, use the this.kmeans.setInit() method to set a initialisation to use you predefined centroids. You'll need to implement a custom FloatKMeansInit subclass that lets you set the centroids externally, but this should be trivial as it only requires implementing a single method.

    Update in response to amended question:

    You shouldn't call initKMeans directly; that happens behind the scenes when you run the algorithm. You also need to populate the centroids array with the centroids initial centroids you want rather than from the bds. For example (untested):

    public class MyFloatKMeansInit extends FloatKMeansInit{
        private float [][] mycentroids;
    
        public MyFloatKMeansInit(float [][] mycentroids) {//modifying data type here
            this.mycentroids = mycentroids;
        }
    
        @Override
        public void initKMeans(DataSource<float[]> bds, float[][] clusters) throws IOException {
            for (int i = 0; i < mycentroids.length; i++) {
                for (int j = 0; j < mycentroids[i].length; j++) {
                    clusters[i][j]=mycentroids[i][j]; //could use arraycopy instead
                }
            }   
        }
    }
    
    public class MyKMColourSegmenter extends KMColourSegmenter{
    
        public MyKMColourSegmenter(ColourSpace colourSpace, float[][] mycentroids) throws IOException {
            super(colourSpace, mycentroids.length);
            MyFloatKMeansInit myFloatKMeansInit = new MyFloatKMeansInit(mycentroids);
            this.kmeans.setInit(myFloatKMeansInit);
        }
    
    }