图像处理------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


二:算法流程

初始输入参数:

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

三:关键代码解析

欧几里德距离计算方法如下:

[java]  view plain copy
  1. private double calculateEuclideanDistance(ClusterPoint p, ClusterCentroid c)   
  2. {  
  3.     // int pa = (p.getPixelColor() >> 24) & 0xff;  
  4.     int pr = (p.getPixelColor() >> 16) & 0xff;  
  5.     int pg = (p.getPixelColor() >> 8) & 0xff;  
  6.     int pb = p.getPixelColor() & 0xff;  
  7.     // int ca = (c.getPixelColor() >> 24) & 0xff;  
  8.     int cr = (c.getPixelColor() >> 16) & 0xff;  
  9.     int cg = (c.getPixelColor() >> 8) & 0xff;  
  10.     int cb = c.getPixelColor() & 0xff;  
  11.       
  12.     return Math.sqrt(Math.pow((pr - cr), 2.0) + Math.pow((pg - cg), 2.0) + Math.pow((pb - cb), 2.0));  
  13. }  

计算每个像素与每个Cluster的Fuzzy数值的代码如下:

[java]  view plain copy
  1. public void stepFuzzy()  
  2. {  
  3.     for (int c = 0; c < this.clusters.size(); c++)  
  4.     {  
  5.         for (int h = 0; h < this.points.size(); h++)  
  6.         {  
  7.   
  8.             double top;  
  9.             top = calculateEuclideanDistance(this.points.get(h), this.clusters.get(c));  
  10.             if (top < 1.0) top = Eps;  
  11.   
  12.             // sumTerms is the sum of distances from this data point to all clusters.  
  13.             double sumTerms = 0.0;  
  14.   
  15.             for (int ck = 0; ck < this.clusters.size(); ck++)  
  16.             {  
  17.                 sumTerms += top / calculateEuclideanDistance(this.points.get(h), this.clusters.get(ck));  
  18.   
  19.             }  
  20.             // Then the membership value can be calculated as...  
  21.             fuzzyForPixels[h][c] = (double)(1.0 / Math.pow(sumTerms, (2 / (this.fuzzy - 1))));   
  22.         }  
  23.     };  
  24.   
  25.   
  26.     this.recalculateClusterMembershipValues();  
  27. }  

计算并更新每个像素的Cluster index的代码如下:

[java]  view plain copy
  1. private void recalculateClusterMembershipValues()   
  2. {  
  3.   
  4.     for (int i = 0; i < this.points.size(); i++)  
  5.    {  
  6.        double max = 0.0;  
  7.        double min = 0.0;  
  8.        double sum = 0.0;  
  9.        double newmax = 0;  
  10.        ClusterPoint p = this.points.get(i);  
  11.        //Normalize the entries  
  12.        for (int j = 0; j < this.clusters.size(); j++)  
  13.        {  
  14.            max = fuzzyForPixels[i][j] > max ? fuzzyForPixels[i][j] : max;  
  15.            min = fuzzyForPixels[i][j] < min ? fuzzyForPixels[i][j] : min;  
  16.        }  
  17.        //Sets the values to the normalized values between 0 and 1  
  18.        for (int j = 0; j < this.clusters.size(); j++)  
  19.        {  
  20.            fuzzyForPixels[i][j] = (fuzzyForPixels[i][j] - min) / (max - min);  
  21.            sum += fuzzyForPixels[i][j];  
  22.        }  
  23.        //Makes it so that the sum of all values is 1   
  24.        for (int j = 0; j < this.clusters.size(); j++)  
  25.        {  
  26.            fuzzyForPixels[i][j] = fuzzyForPixels[i][j] / sum;  
  27.            if (Double.isNaN(fuzzyForPixels[i][j]))  
  28.            {  
  29.                fuzzyForPixels[i][j] = 0.0;  
  30.            }  
  31.            newmax = fuzzyForPixels[i][j] > newmax ? fuzzyForPixels[i][j] : newmax;  
  32.        }  
  33.        // ClusterIndex is used to store the strongest membership value to a cluster, used for defuzzification  
  34.         p.setClusterIndex(newmax);  
  35.      };  
  36. }  

四:运行效果


五:算法源代码

FuzzyCMeansProcessor - 算法类

[java]  view plain copy
  1. package com.gloomyfish.segmentation.fuzzycmeans;  
  2.   
  3. import java.awt.image.BufferedImage;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6. import java.util.Random;  
  7.   
  8. import com.gloomyfish.filter.study.AbstractBufferedImageOp;  
  9.   
  10. public class FuzzyCMeansProcessor extends AbstractBufferedImageOp {  
  11.       
  12.     private List<ClusterPoint> points;  
  13.     private List<ClusterCentroid> clusters;  
  14.     private BufferedImage originalImage;  
  15.     private BufferedImage processedImage;  
  16.     private double Eps = Math.pow(10, -5);  
  17.   
  18.     private double[][] fuzzyForPixels;  
  19.       
  20.     // Gets or sets objective function  
  21.     private double numObj;  
  22.       
  23.     public void setObj(double j) {  
  24.         this.numObj = j;  
  25.     }  
  26.       
  27.     public double getObj() {  
  28.         return this.numObj;  
  29.     }  
  30.       
  31.     private float fuzzy; // default is 2  
  32.     private int numCluster; // number of clusters in image  
  33.       
  34.     public BufferedImage getResultImage()  
  35.     {  
  36.         return this.processedImage;  
  37.     }  
  38.       
  39.     public FuzzyCMeansProcessor(/*List<ClusterPoint> points, List<ClusterCentroid> clusters, */float fuzzy, BufferedImage myImage, int numCluster)  
  40.     {  
  41.         points = new ArrayList<ClusterPoint>();  
  42.   
  43.         int width = myImage.getWidth();  
  44.         int height = myImage.getHeight();  
  45.         int index = 0;  
  46.         int[] inPixels = new int[width*height];  
  47.         myImage.getRGB( 00, width, height, inPixels, 0, width );  
  48.         for (int row = 0; row < myImage.getHeight(); ++row)  
  49.         {  
  50.             for (int col = 0; col < myImage.getWidth(); ++col)  
  51.             {  
  52.                 index = row * width + col;  
  53.                 int color = inPixels[index];  
  54.                 points.add(new ClusterPoint(row, col, color));  
  55.   
  56.             }  
  57.         }  
  58.   
  59.   
  60.   
  61.         clusters = new ArrayList<ClusterCentroid>();  
  62.          
  63.         //Create random points to use a the cluster centroids  
  64.         Random random = new Random();  
  65.         for (int i = 0; i < numCluster; i++)  
  66.         {  
  67.             int randomNumber1 = random.nextInt(width);  
  68.             int randomNumber2 = random.nextInt(height);  
  69.             index = randomNumber2 * width + randomNumber1;  
  70.             clusters.add(new ClusterCentroid(randomNumber1, randomNumber2, inPixels[index]));   
  71.         }  
  72.           
  73.         this.originalImage = myImage;  
  74.         this.fuzzy = fuzzy;  
  75.         this.numCluster = numCluster;  
  76.           
  77.         double diff;  
  78.   
  79.         // Iterate through all points to create initial U matrix  
  80.         fuzzyForPixels = new double[this.points.size()][this.clusters.size()];  
  81.         for (int i = 0; i < this.points.size(); i++)  
  82.         {  
  83.             ClusterPoint p = points.get(i);  
  84.             double sum = 0.0;  
  85.   
  86.             for (int j = 0; j < this.clusters.size(); j++)  
  87.             {  
  88.                 ClusterCentroid c = this.clusters.get(j);  
  89.                 diff = Math.sqrt(Math.pow(calculateEuclideanDistance(p, c), 2.0));  
  90.                 fuzzyForPixels[i][j] = (diff == 0) ? Eps : diff;  
  91.                 sum += fuzzyForPixels[i][j];  
  92.             }  
  93.          }  
  94.           
  95.         // re-calculate the membership value for one point of all clusters, and make suer it's sum of value is 1  
  96.         recalculateClusterMembershipValues();  
  97.           
  98.     }  
  99.       
  100.     public void calculateClusterCentroids()  
  101.     {  
  102.         for (int j = 0; j < this.clusters.size(); j++)  
  103.         {  
  104.             ClusterCentroid clusterCentroid = this.clusters.get(j);  
  105.               
  106.             double l = 0.0;  
  107.             clusterCentroid.setRedSum(0);  
  108.             clusterCentroid.setBlueSum(0);  
  109.             clusterCentroid.setGreenSum(0);  
  110.             clusterCentroid.setMemberShipSum(0);  
  111.             double redSum = 0;  
  112.             double greenSum = 0;  
  113.             double blueSum = 0;  
  114.             double memebershipSum = 0;  
  115.             double pixelCount = 1;  
  116.   
  117.             for (int i = 0; i < this.points.size(); i++)  
  118.             {  
  119.               
  120.                 ClusterPoint p = this.points.get(i);  
  121.                 l = Math.pow(fuzzyForPixels[i][j], this.fuzzy);  
  122.                 int ta = (p.getPixelColor() >> 24) & 0xff;  
  123.                 int tr = (p.getPixelColor() >> 16) & 0xff;  
  124.                 int tg = (p.getPixelColor() >> 8) & 0xff;  
  125.                 int tb = p.getPixelColor() & 0xff;  
  126.                 redSum += l * tr;  
  127.                 greenSum += l * tg;  
  128.                 blueSum += l * tb;  
  129.                 memebershipSum += l;  
  130.   
  131.                 if (fuzzyForPixels[i][j] == p.getClusterIndex())  
  132.                 {  
  133.                     pixelCount += 1;  
  134.                 }  
  135.             }  
  136.               
  137.             int clusterColor = (255 << 24) | ((int)(redSum / memebershipSum) << 16) | ((int)(greenSum / memebershipSum) << 8) | (int)(blueSum / memebershipSum);  
  138.             clusterCentroid.setPixelColor(clusterColor);  
  139.          }  
  140.   
  141.         //update the original image  
  142.         // Bitmap tempImage = new Bitmap(myImageWidth, myImageHeight, PixelFormat.Format32bppRgb);  
  143.         BufferedImage tempImage = createCompatibleDestImage( originalImage, null );  
  144.         int width = tempImage.getWidth();  
  145.         int height = tempImage.getHeight();  
  146.         int index = 0;  
  147.         int[] outPixels = new int[width*height];  
  148.           
  149.         for (int j = 0; j < this.points.size(); j++)  
  150.         {  
  151.             for (int i = 0; i < this.clusters.size(); i++)  
  152.             {  
  153.                 ClusterPoint p = this.points.get(j);  
  154.                 if (fuzzyForPixels[j][i] == p.getClusterIndex())  
  155.                 {  
  156.                     int row = (int)p.getX(); // row  
  157.                     int col = (int)p.getY(); // column  
  158.                     index = row * width + col;  
  159.                     outPixels[index] = this.clusters.get(i).getPixelColor();  
  160.                 }  
  161.             }  
  162.         }  
  163.           
  164.         // fill the pixel data  
  165.         setRGB( tempImage, 00, width, height, outPixels );  
  166.         processedImage = tempImage;  
  167.     }  
  168.       
  169.     /// <summary>  
  170.     /// Perform one step of the algorithm  
  171.     /// </summary>  
  172.     public void stepFuzzy()  
  173.     {  
  174.         for (int c = 0; c < this.clusters.size(); c++)  
  175.         {  
  176.             for (int h = 0; h < this.points.size(); h++)  
  177.             {  
  178.   
  179.                 double top;  
  180.                 top = calculateEuclideanDistance(this.points.get(h), this.clusters.get(c));  
  181.                 if (top < 1.0) top = Eps;  
  182.   
  183.                 // sumTerms is the sum of distances from this data point to all clusters.  
  184.                 double sumTerms = 0.0;  
  185.   
  186.                 for (int ck = 0; ck < this.clusters.size(); ck++)  
  187.                 {  
  188.                     sumTerms += top / calculateEuclideanDistance(this.points.get(h), this.clusters.get(ck));  
  189.   
  190.                 }  
  191.                 // Then the membership value can be calculated as...  
  192.                 fuzzyForPixels[h][c] = (double)(1.0 / Math.pow(sumTerms, (2 / (this.fuzzy - 1))));   
  193.             }  
  194.         };  
  195.   
  196.   
  197.         this.recalculateClusterMembershipValues();  
  198.     }  
  199.       
  200.     public double calculateObjectiveFunction()  
  201.     {  
  202.         double Jk = 0.0;  
  203.   
  204.         for (int i = 0; i < this.points.size();i++)  
  205.         {  
  206.             for (int j = 0; j < this.clusters.size(); j++)  
  207.             {  
  208.                 Jk += Math.pow(fuzzyForPixels[i][j], this.fuzzy) * Math.pow(this.calculateEuclideanDistance(points.get(i), clusters.get(j)), 2);  
  209.             }  
  210.         }  
  211.         return Jk;  
  212.     }  
  213.       
  214.       
  215.     private void recalculateClusterMembershipValues()   
  216.     {  
  217.       
  218.         for (int i = 0; i < this.points.size(); i++)  
  219.        {  
  220.            double max = 0.0;  
  221.            double min = 0.0;  
  222.            double sum = 0.0;  
  223.            double newmax = 0;  
  224.            ClusterPoint p = this.points.get(i);  
  225.            //Normalize the entries  
  226.            for (int j = 0; j < this.clusters.size(); j++)  
  227.            {  
  228.                max = fuzzyForPixels[i][j] > max ? fuzzyForPixels[i][j] : max;  
  229.                min = fuzzyForPixels[i][j] < min ? fuzzyForPixels[i][j] : min;  
  230.            }  
  231.            //Sets the values to the normalized values between 0 and 1  
  232.            for (int j = 0; j < this.clusters.size(); j++)  
  233.            {  
  234.                fuzzyForPixels[i][j] = (fuzzyForPixels[i][j] - min) / (max - min);  
  235.                sum += fuzzyForPixels[i][j];  
  236.            }  
  237.            //Makes it so that the sum of all values is 1   
  238.            for (int j = 0; j < this.clusters.size(); j++)  
  239.            {  
  240.                fuzzyForPixels[i][j] = fuzzyForPixels[i][j] / sum;  
  241.                if (Double.isNaN(fuzzyForPixels[i][j]))  
  242.                {  
  243.                    fuzzyForPixels[i][j] = 0.0;  
  244.                }  
  245.                newmax = fuzzyForPixels[i][j] > newmax ? fuzzyForPixels[i][j] : newmax;  
  246.            }  
  247.            // ClusterIndex is used to store the strongest membership value to a cluster, used for defuzzification  
  248.             p.setClusterIndex(newmax);  
  249.          };  
  250.     }  
  251.   
  252.     private double calculateEuclideanDistance(ClusterPoint p, ClusterCentroid c)   
  253.     {  
  254.         // int pa = (p.getPixelColor() >> 24) & 0xff;  
  255.         int pr = (p.getPixelColor() >> 16) & 0xff;  
  256.         int pg = (p.getPixelColor() >> 8) & 0xff;  
  257.         int pb = p.getPixelColor() & 0xff;  
  258.         // int ca = (c.getPixelColor() >> 24) & 0xff;  
  259.         int cr = (c.getPixelColor() >> 16) & 0xff;  
  260.         int cg = (c.getPixelColor() >> 8) & 0xff;  
  261.         int cb = c.getPixelColor() & 0xff;  
  262.           
  263.         return Math.sqrt(Math.pow((pr - cr), 2.0) + Math.pow((pg - cg), 2.0) + Math.pow((pb - cb), 2.0));  
  264.     }  
  265.   
  266.     @Override  
  267.     public BufferedImage filter(BufferedImage src, BufferedImage dest) {  
  268.         return processedImage;  
  269.     }  
  270.   
  271. }  
ClusterPoint- 存储图像像素点对象

[java]  view plain copy
  1. package com.gloomyfish.segmentation.fuzzycmeans;  
  2.   
  3. public class ClusterPoint {  
  4.     private double x;  
  5.     private double y;  
  6.     private int pixelColor;  
  7.     private int originalPixelColor;  
  8.     private double clusterIndex;  
  9.       
  10.     public ClusterPoint(double x, double y, int col)  
  11.     {  
  12.         this.x = x;  
  13.         this.y = y;  
  14.         this.pixelColor = col;  
  15.         this.originalPixelColor = col;  
  16.         this.clusterIndex = -1;  
  17.     }  
  18.       
  19.     public double getX() {  
  20.         return x;  
  21.     }  
  22.   
  23.     public void setX(double x) {  
  24.         this.x = x;  
  25.     }  
  26.   
  27.     public double getY() {  
  28.         return y;  
  29.     }  
  30.   
  31.     public void setY(double y) {  
  32.         this.y = y;  
  33.     }  
  34.   
  35.     public int getPixelColor() {  
  36.         return pixelColor;  
  37.     }  
  38.   
  39.     public void setPixelColor(int pixelColor) {  
  40.         this.pixelColor = pixelColor;  
  41.     }  
  42.   
  43.     public int getOriginalPixelColor() {  
  44.         return originalPixelColor;  
  45.     }  
  46.   
  47.     public void setOriginalPixelColor(int originalPixelColor) {  
  48.         this.originalPixelColor = originalPixelColor;  
  49.     }  
  50.   
  51.     public double getClusterIndex() {  
  52.         return clusterIndex;  
  53.     }  
  54.   
  55.     public void setClusterIndex(double clusterIndex) {  
  56.         this.clusterIndex = clusterIndex;  
  57.     }  
  58.   
  59. }  
ClusterCentroid - 存储Cluster信息对象

[java]  view plain copy
  1. package com.gloomyfish.segmentation.fuzzycmeans;  
  2.   
  3. public class ClusterCentroid {  
  4.   
  5.     private double x;  
  6.     private double y;  
  7.     private int pixelColor;  
  8.     private double redSum;  
  9.     private double greenSum;  
  10.     private double blueSum;  
  11.     private double memberShipSum;  
  12.     private int originalPixelColor;  
  13.       
  14.     public ClusterCentroid(double x, double y, int color)  
  15.     {  
  16.         this.x = x;  
  17.         this.y = y;  
  18.         this.originalPixelColor = color;  
  19.         this.pixelColor = color;  
  20.     }  
  21.       
  22.     public double getX() {  
  23.         return x;  
  24.     }  
  25.   
  26.     public void setX(double x) {  
  27.         this.x = x;  
  28.     }  
  29.   
  30.     public double getY() {  
  31.         return y;  
  32.     }  
  33.   
  34.     public void setY(double y) {  
  35.         this.y = y;  
  36.     }  
  37.   
  38.     public int getPixelColor() {  
  39.         return pixelColor;  
  40.     }  
  41.   
  42.     public void setPixelColor(int pixelColor) {  
  43.         this.pixelColor = pixelColor;  
  44.     }  
  45.   
  46.     public double getRedSum() {  
  47.         return redSum;  
  48.     }  
  49.   
  50.     public void setRedSum(double redSum) {  
  51.         this.redSum = redSum;  
  52.     }  
  53.   
  54.     public double getGreenSum() {  
  55.         return greenSum;  
  56.     }  
  57.   
  58.     public void setGreenSum(double greenSum) {  
  59.         this.greenSum = greenSum;  
  60.     }  
  61.   
  62.     public double getBlueSum() {  
  63.         return blueSum;  
  64.     }  
  65.   
  66.     public void setBlueSum(double blueSum) {  
  67.         this.blueSum = blueSum;  
  68.     }  
  69.   
  70.     public double getMemberShipSum() {  
  71.         return memberShipSum;  
  72.     }  
  73.   
  74.     public void setMemberShipSum(double memberShipSum) {  
  75.         this.memberShipSum = memberShipSum;  
  76.     }  
  77.   
  78.     public int getOriginalPixelColor() {  
  79.         return originalPixelColor;  
  80.     }  
  81.   
  82.     public void setOriginalPixelColor(int originalPixelColor) {  
  83.         this.originalPixelColor = originalPixelColor;  
  84.     }  
  85.   
  86. }  

算法调用:

[java]  view plain copy
  1.   int numClusters = 2// (int)numericUpDown2.Value;  
  2.   int maxIterations = 20//(int)numericUpDown3.Value;  
  3.   double accuracy = 0.00001// (double)numericUpDown4.Value;  
  4.   FuzzyCMeansProcessor alg = new FuzzyCMeansProcessor(numClusters, sourceImage, numClusters);  
  5.   int k = 0;  
  6.   do  
  7.   {  
  8.       k++;  
  9.       alg.setObj(alg.calculateObjectiveFunction());  
  10.       alg.calculateClusterCentroids();  
  11.       alg.stepFuzzy();  
  12.       double Jnew = alg.calculateObjectiveFunction();  
  13.       System.out.println("Run method accuracy of delta value = " + Math.abs(alg.getObj() - Jnew));  
  14.       if (Math.abs(alg.getObj() - Jnew) < accuracy) break;  
  15.   }  
  16.   while (maxIterations > k);  
  17.   resultImage = alg.getResultImage();  
  18.   this.repaint();  
  19. }  

六:Fuzzy C-means不足之处

需要提供额外的参数,不能自动识别Cluster,运行时间比较长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值