图像处理之Fuzzy C Means的聚合算法

简介: 图像处理之Fuzzy C Means的聚合算法

Fuzzy C-Means聚合算法在图像分割(segmentation)和图像视觉处理中常常被用到聚合算法之


一本文是完全基于JAVA语言实现Fuzzy C-Means聚合算法,并可以运用到图像处理中实现简


单的对象提取。




一:数学原理


在解释数学原理之前,请先看看这个链接算是热身吧


http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/cmeans.html


看不懂没关系。我的解释足够详细,小学毕业都可以学会,本人就是小学毕业。


Fuzzy C-means算法主要是比较RGB空间的每个像素值与Cluster中的每个中心点值,最终给


每个像素指派一个值(0~1之间)说明该像素更接近于哪里Cluster的中心点,模糊规则是该像


素对所有cluster的值之和为1。简单的举例:假设图像中有三个聚类cluster1,cluster2,cluster3,


像素A对三个聚类的值分别为a1, a2, a3, 根据模糊规则a1 + a2 + a3 = 1。更进一步,如果a1


最大,则该像素比较接近于Cluster1。计算总的对象值J:

1365927277_5974.png


二:算法流程


初始输入参数:


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
       for (int j = 0; j < this.clusters.size(); j++)
       {
         fuzzyForPixels[i][j] = (fuzzyForPixels[i][j] - min) / (max - min);
           sum += fuzzyForPixels[i][j];
       }
       //Makes it so that the sum of all values is 1 
       for (int j = 0; j < this.clusters.size(); j++)
       {
         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);
     };
}

四:运行效果

1365927873_9248.png

五:算法源代码

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 index = 0;
        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++)
        {
            for (int j = 0; j < this.clusters.size(); j++)
            {
                Jk += Math.pow(fuzzyForPixels[i][j], this.fuzzy) * Math.pow(this.calculateEuclideanDistance(points.get(i), clusters.get(j)), 2);
            }
        }
        return Jk;
    }
    
  
  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
         for (int j = 0; j < this.clusters.size(); j++)
         {
           fuzzyForPixels[i][j] = (fuzzyForPixels[i][j] - min) / (max - min);
             sum += fuzzyForPixels[i][j];
         }
         //Makes it so that the sum of all values is 1 
         for (int j = 0; j < this.clusters.size(); j++)
         {
           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- 存储图像像素点对象

package com.gloomyfish.segmentation.fuzzycmeans;
 
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) {
    this.x = x;
  }
 
  public double getY() {
    return y;
  }
 
  public void setY(double y) {
    this.y = 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信息对象

package com.gloomyfish.segmentation.fuzzycmeans;
 
public class ClusterCentroid {
 
  private double x;
  private double y;
  private int pixelColor;
  private double redSum;
  private double greenSum;
  private double blueSum;
  private double memberShipSum;
  private int originalPixelColor;
  
    public ClusterCentroid(double x, double y, int color)
    {
      this.x = x;
      this.y = y;
      this.originalPixelColor = color;
      this.pixelColor = color;
    }
    
  public double getX() {
    return x;
  }
 
  public void setX(double x) {
    this.x = x;
  }
 
  public double getY() {
    return y;
  }
 
  public void setY(double y) {
    this.y = y;
  }
 
  public int getPixelColor() {
    return pixelColor;
  }
 
  public void setPixelColor(int pixelColor) {
    this.pixelColor = pixelColor;
  }
 
  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;
  }
 
  public int getOriginalPixelColor() {
    return originalPixelColor;
  }
 
  public void setOriginalPixelColor(int originalPixelColor) {
    this.originalPixelColor = originalPixelColor;
  }
 
}

算法调用:

  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,运行时间比较长。

博客从本月开始更新,请关注!!谢谢谢!

相关文章
|
5月前
|
算法 计算机视觉
图像处理之积分图应用四(基于局部均值的图像二值化算法)
图像处理之积分图应用四(基于局部均值的图像二值化算法)
536 0
|
5月前
|
监控 算法 图计算
图像处理之积分图应用三(基于NCC快速相似度匹配算法)
图像处理之积分图应用三(基于NCC快速相似度匹配算法)
73 0
|
5月前
|
文字识别 算法 计算机视觉
图像处理之Zhang Suen细化算法
图像处理之Zhang Suen细化算法
76 0
|
5月前
|
算法 Java 计算机视觉
图像处理之积分图算法
图像处理之积分图算法
64 2
|
5月前
|
资源调度 算法 计算机视觉
图像处理之积分图应用二(快速边缘保留滤波算法)
图像处理之积分图应用二(快速边缘保留滤波算法)
36 0
|
5月前
|
算法 BI 计算机视觉
图像处理之积分图应用一(半径无关的快速模糊算法)
图像处理之积分图应用一(半径无关的快速模糊算法)
44 0
|
5月前
|
算法 计算机视觉
图像处理之基于泛红算法的二值图像内部区域填充
图像处理之基于泛红算法的二值图像内部区域填充
44 0
|
24天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
9天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
10天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。