天天看點

圖像處理------Fuzzy C Means的聚合算法

fuzzy c-means聚合算法在圖像分割(segmentation)和圖像視覺進行中常常被用到聚合算法之

一本文是完全基于java語言實作fuzzy c-means聚合算法,并可以運用到圖像進行中實作簡

單的對象提取。

一:數學原理

在解釋數學原理之前,請先看看這個連結算是熱身吧

看不懂沒關系。我的解釋足夠詳細,國小畢業都可以學會,本人就是國小畢業。

fuzzy c-means算法主要是比較rgb空間的每個像素值與cluster中的每個中心點值,最終給

每個像素指派一個值(0~1之間)說明該像素更接近于哪裡cluster的中心點,模糊規則是該像

素對所有cluster的值之和為1。簡單的舉例:假設圖像中有三個聚類cluster1,cluster2,cluster3,

像素a對三個聚類的值分别為a1, a2, a3, 根據模糊規則a1 + a2 + a3 = 1。更進一步,如果a1

最大,則該像素比較接近于cluster1。計算總的對象值j:

圖像處理------Fuzzy C Means的聚合算法

二:算法流程

初始輸入參數:

a.      指定的聚類個數numberofclusters,

b.      指定的最大循環次數maxiteration

c.      指定的最小終止循環內插補點deltavalue

大緻流程如下:

1.      初始化所有像素點值與随機選取每個cluster的中心點,初始化每個像素點p[i]對應

cluster的模糊值p[i][k]并計算cluster index。

2.      計算對象值j

3.      計算每個cluster的顔色值,産生新的圖像像素

4.      計算每個像素的對應每個cluster的模糊值,更新每個像素的cluster index

5.      再次計算對象值j,并與第二步的對象值相減,如果內插補點小于deltavalue或者達到最大

循環數,停止計算輸出結果圖像,否則繼續2 ~ 4

三:關鍵代碼解析

歐幾裡德距離計算方法如下:

private double calculateeuclideandistance(clusterpoint p, clustercentroid c)   

{  

    // int pa = (p.getpixelcolor() >> 24) & 0xff;  

    int pr = (p.getpixelcolor() >> 16) & 0xff;  

    int pg = (p.getpixelcolor() >> 8) & 0xff;  

    int pb = p.getpixelcolor() & 0xff;  

    // int ca = (c.getpixelcolor() >> 24) & 0xff;  

    int cr = (c.getpixelcolor() >> 16) & 0xff;  

    int cg = (c.getpixelcolor() >> 8) & 0xff;  

    int cb = c.getpixelcolor() & 0xff;  

    return math.sqrt(math.pow((pr - cr), 2.0) + math.pow((pg - cg), 2.0) + math.pow((pb - cb), 2.0));  

}  

計算每個像素與每個cluster的fuzzy數值的代碼如下:

public void stepfuzzy()  

    for (int c = 0; c < this.clusters.size(); c++)  

    {  

        for (int h = 0; h < this.points.size(); h++)  

        {  

            double top;  

            top = calculateeuclideandistance(this.points.get(h), this.clusters.get(c));  

            if (top < 1.0) top = eps;  

            // sumterms is the sum of distances from this data point to all clusters.  

            double sumterms = 0.0;  

            for (int ck = 0; ck < this.clusters.size(); ck++)  

            {  

                sumterms += top / calculateeuclideandistance(this.points.get(h), this.clusters.get(ck));  

            }  

            // then the membership value can be calculated as...  

            fuzzyforpixels[h][c] = (double)(1.0 / math.pow(sumterms, (2 / (this.fuzzy - 1))));   

        }  

    };  

    this.recalculateclustermembershipvalues();  

計算并更新每個像素的cluster index的代碼如下:

private void recalculateclustermembershipvalues()   

    for (int i = 0; i < this.points.size(); i++)  

   {  

       double max = 0.0;  

       double min = 0.0;  

       double sum = 0.0;  

       double newmax = 0;  

       clusterpoint p = this.points.get(i);  

       //normalize the entries  

       for (int j = 0; j < this.clusters.size(); j++)  

       {  

           max = fuzzyforpixels[i][j] > max ? fuzzyforpixels[i][j] : max;  

           min = fuzzyforpixels[i][j] < min ? fuzzyforpixels[i][j] : min;  

       }  

       //sets the values to the normalized values between 0 and 1  

           fuzzyforpixels[i][j] = (fuzzyforpixels[i][j] - min) / (max - min);  

           sum += fuzzyforpixels[i][j];  

       //makes it so that the sum of all values is 1   

           fuzzyforpixels[i][j] = fuzzyforpixels[i][j] / sum;  

           if (double.isnan(fuzzyforpixels[i][j]))  

           {  

               fuzzyforpixels[i][j] = 0.0;  

           }  

           newmax = fuzzyforpixels[i][j] > newmax ? fuzzyforpixels[i][j] : newmax;  

       // clusterindex is used to store the strongest membership value to a cluster, used for defuzzification  

        p.setclusterindex(newmax);  

     };  

四:運作效果

圖像處理------Fuzzy C Means的聚合算法

五:算法源代碼

fuzzycmeansprocessor - 算法類

package com.gloomyfish.segmentation.fuzzycmeans;  

import java.awt.image.bufferedimage;  

import java.util.arraylist;  

import java.util.list;  

import java.util.random;  

import com.gloomyfish.filter.study.abstractbufferedimageop;  

public class fuzzycmeansprocessor extends abstractbufferedimageop {  

    private list<clusterpoint> points;  

    private list<clustercentroid> clusters;  

    private bufferedimage originalimage;  

    private bufferedimage processedimage;  

    private double eps = math.pow(10, -5);  

    private double[][] fuzzyforpixels;  

    // gets or sets objective function  

    private double numobj;  

    public void setobj(double j) {  

        this.numobj = j;  

    }  

    public double getobj() {  

        return this.numobj;  

    private float fuzzy; // default is 2  

    private int numcluster; // number of clusters in image  

    public bufferedimage getresultimage()  

        return this.processedimage;  

    public fuzzycmeansprocessor(/*list<clusterpoint> points, list<clustercentroid> clusters, */float fuzzy, bufferedimage myimage, int numcluster)  

        points = new arraylist<clusterpoint>();  

        int width = myimage.getwidth();  

        int height = myimage.getheight();  

        int index = 0;  

        int[] inpixels = new int[width*height];  

        myimage.getrgb( 0, 0, width, height, inpixels, 0, width );  

        for (int row = 0; row < myimage.getheight(); ++row)  

            for (int col = 0; col < myimage.getwidth(); ++col)  

                index = row * width + col;  

                int color = inpixels[index];  

                points.add(new clusterpoint(row, col, color));  

        clusters = new arraylist<clustercentroid>();  

        //create random points to use a the cluster centroids  

        random random = new random();  

        for (int i = 0; i < numcluster; i++)  

            int randomnumber1 = random.nextint(width);  

            int randomnumber2 = random.nextint(height);  

            index = randomnumber2 * width + randomnumber1;  

            clusters.add(new clustercentroid(randomnumber1, randomnumber2, inpixels[index]));   

        this.originalimage = myimage;  

        this.fuzzy = fuzzy;  

        this.numcluster = numcluster;  

        double diff;  

        // iterate through all points to create initial u matrix  

        fuzzyforpixels = new double[this.points.size()][this.clusters.size()];  

        for (int i = 0; i < this.points.size(); i++)  

            clusterpoint p = points.get(i);  

            double sum = 0.0;  

            for (int j = 0; j < this.clusters.size(); j++)  

                clustercentroid c = this.clusters.get(j);  

                diff = math.sqrt(math.pow(calculateeuclideandistance(p, c), 2.0));  

                fuzzyforpixels[i][j] = (diff == 0) ? eps : diff;  

                sum += fuzzyforpixels[i][j];  

         }  

        // re-calculate the membership value for one point of all clusters, and make suer it's sum of value is 1  

        recalculateclustermembershipvalues();  

    public void calculateclustercentroids()  

        for (int j = 0; j < this.clusters.size(); j++)  

            clustercentroid clustercentroid = this.clusters.get(j);  

            double l = 0.0;  

            clustercentroid.setredsum(0);  

            clustercentroid.setbluesum(0);  

            clustercentroid.setgreensum(0);  

            clustercentroid.setmembershipsum(0);  

            double redsum = 0;  

            double greensum = 0;  

            double bluesum = 0;  

            double memebershipsum = 0;  

            double pixelcount = 1;  

            for (int i = 0; i < this.points.size(); i++)  

                clusterpoint p = this.points.get(i);  

                l = math.pow(fuzzyforpixels[i][j], this.fuzzy);  

                int ta = (p.getpixelcolor() >> 24) & 0xff;  

                int tr = (p.getpixelcolor() >> 16) & 0xff;  

                int tg = (p.getpixelcolor() >> 8) & 0xff;  

                int tb = p.getpixelcolor() & 0xff;  

                redsum += l * tr;  

                greensum += l * tg;  

                bluesum += l * tb;  

                memebershipsum += l;  

                if (fuzzyforpixels[i][j] == p.getclusterindex())  

                {  

                    pixelcount += 1;  

                }  

            int clustercolor = (255 << 24) | ((int)(redsum / memebershipsum) << 16) | ((int)(greensum / memebershipsum) << 8) | (int)(bluesum / memebershipsum);  

            clustercentroid.setpixelcolor(clustercolor);  

        //update the original image  

        // bitmap tempimage = new bitmap(myimagewidth, myimageheight, pixelformat.format32bpprgb);  

        bufferedimage tempimage = createcompatibledestimage( originalimage, null );  

        int width = tempimage.getwidth();  

        int height = tempimage.getheight();  

        int[] outpixels = new int[width*height];  

        for (int j = 0; j < this.points.size(); j++)  

            for (int i = 0; i < this.clusters.size(); i++)  

                clusterpoint p = this.points.get(j);  

                if (fuzzyforpixels[j][i] == p.getclusterindex())  

                    int row = (int)p.getx(); // row  

                    int col = (int)p.gety(); // column  

                    index = row * width + col;  

                    outpixels[index] = this.clusters.get(i).getpixelcolor();  

        // fill the pixel data  

        setrgb( tempimage, 0, 0, width, height, outpixels );  

        processedimage = tempimage;  

    /// <summary>  

    /// perform one step of the algorithm  

    /// </summary>  

    public void stepfuzzy()  

        for (int c = 0; c < this.clusters.size(); c++)  

            for (int h = 0; h < this.points.size(); h++)  

                double top;  

                top = calculateeuclideandistance(this.points.get(h), this.clusters.get(c));  

                if (top < 1.0) top = eps;  

                // sumterms is the sum of distances from this data point to all clusters.  

                double sumterms = 0.0;  

                for (int ck = 0; ck < this.clusters.size(); ck++)  

                    sumterms += top / calculateeuclideandistance(this.points.get(h), this.clusters.get(ck));  

                // then the membership value can be calculated as...  

                fuzzyforpixels[h][c] = (double)(1.0 / math.pow(sumterms, (2 / (this.fuzzy - 1))));   

        };  

        this.recalculateclustermembershipvalues();  

    public double calculateobjectivefunction()  

        double jk = 0.0;  

        for (int i = 0; i < this.points.size();i++)  

                jk += math.pow(fuzzyforpixels[i][j], this.fuzzy) * math.pow(this.calculateeuclideandistance(points.get(i), clusters.get(j)), 2);  

        return jk;  

    private void recalculateclustermembershipvalues()   

           double max = 0.0;  

           double min = 0.0;  

           double sum = 0.0;  

           double newmax = 0;  

           clusterpoint p = this.points.get(i);  

           //normalize the entries  

           for (int j = 0; j < this.clusters.size(); j++)  

               max = fuzzyforpixels[i][j] > max ? fuzzyforpixels[i][j] : max;  

               min = fuzzyforpixels[i][j] < min ? fuzzyforpixels[i][j] : min;  

           //sets the values to the normalized values between 0 and 1  

               fuzzyforpixels[i][j] = (fuzzyforpixels[i][j] - min) / (max - min);  

               sum += fuzzyforpixels[i][j];  

           //makes it so that the sum of all values is 1   

               fuzzyforpixels[i][j] = fuzzyforpixels[i][j] / sum;  

               if (double.isnan(fuzzyforpixels[i][j]))  

               {  

                   fuzzyforpixels[i][j] = 0.0;  

               }  

               newmax = fuzzyforpixels[i][j] > newmax ? fuzzyforpixels[i][j] : newmax;  

           // clusterindex is used to store the strongest membership value to a cluster, used for defuzzification  

            p.setclusterindex(newmax);  

         };  

    private double calculateeuclideandistance(clusterpoint p, clustercentroid c)   

        // int pa = (p.getpixelcolor() >> 24) & 0xff;  

        int pr = (p.getpixelcolor() >> 16) & 0xff;  

        int pg = (p.getpixelcolor() >> 8) & 0xff;  

        int pb = p.getpixelcolor() & 0xff;  

        // int ca = (c.getpixelcolor() >> 24) & 0xff;  

        int cr = (c.getpixelcolor() >> 16) & 0xff;  

        int cg = (c.getpixelcolor() >> 8) & 0xff;  

        int cb = c.getpixelcolor() & 0xff;  

        return math.sqrt(math.pow((pr - cr), 2.0) + math.pow((pg - cg), 2.0) + math.pow((pb - cb), 2.0));  

    @override  

    public bufferedimage filter(bufferedimage src, bufferedimage dest) {  

        return processedimage;  

clusterpoint- 存儲圖像像素點對象

public class clusterpoint {  

    private double x;  

    private double y;  

    private int pixelcolor;  

    private int originalpixelcolor;  

    private double clusterindex;  

    public clusterpoint(double x, double y, int col)  

        this.x = x;  

        this.y = y;  

        this.pixelcolor = col;  

        this.originalpixelcolor = col;  

        this.clusterindex = -1;  

    public double getx() {  

        return x;  

    public void setx(double x) {  

    public double gety() {  

        return y;  

    public void sety(double y) {  

    public int getpixelcolor() {  

        return pixelcolor;  

    public void setpixelcolor(int pixelcolor) {  

        this.pixelcolor = pixelcolor;  

    public int getoriginalpixelcolor() {  

        return originalpixelcolor;  

    public void setoriginalpixelcolor(int originalpixelcolor) {  

        this.originalpixelcolor = originalpixelcolor;  

    public double getclusterindex() {  

        return clusterindex;  

    public void setclusterindex(double clusterindex) {  

        this.clusterindex = clusterindex;  

clustercentroid - 存儲cluster資訊對象

public class clustercentroid {  

    private double redsum;  

    private double greensum;  

    private double bluesum;  

    private double membershipsum;  

    public clustercentroid(double x, double y, int color)  

        this.originalpixelcolor = color;  

        this.pixelcolor = color;  

    public double getredsum() {  

        return redsum;  

    public void setredsum(double redsum) {  

        this.redsum = redsum;  

    public double getgreensum() {  

        return greensum;  

    public void setgreensum(double greensum) {  

        this.greensum = greensum;  

    public double getbluesum() {  

        return bluesum;  

    public void setbluesum(double bluesum) {  

        this.bluesum = bluesum;  

    public double getmembershipsum() {  

        return membershipsum;  

    public void setmembershipsum(double membershipsum) {  

        this.membershipsum = membershipsum;  

算法調用:

  int numclusters = 2; // (int)numericupdown2.value;  

  int maxiterations = 20; //(int)numericupdown3.value;  

  double accuracy = 0.00001; // (double)numericupdown4.value;  

  fuzzycmeansprocessor alg = new fuzzycmeansprocessor(numclusters, sourceimage, numclusters);  

  int k = 0;  

  do  

  {  

      k++;  

      alg.setobj(alg.calculateobjectivefunction());  

      alg.calculateclustercentroids();  

      alg.stepfuzzy();  

      double jnew = alg.calculateobjectivefunction();  

      system.out.println("run method accuracy of delta value = " + math.abs(alg.getobj() - jnew));  

      if (math.abs(alg.getobj() - jnew) < accuracy) break;  

  }  

  while (maxiterations > k);  

  resultimage = alg.getresultimage();  

  this.repaint();  

六:fuzzy c-means不足之處

需要提供額外的參數,不能自動識别cluster,運作時間比較長。