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

简介: Fuzzy C-Means聚合算法在图像分割(segmentation)和图像视觉处理中常常被用到聚合算法之 一本文是完全基于JAVA语言实现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


二:算法流程

初始输入参数:

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);
     };
}

四:运行效果


五:算法源代码

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

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

目录
相关文章
|
2月前
|
算法 计算机视觉
图像处理常用算法—6个算子 !!
图像处理常用算法—6个算子 !!
32 2
|
7月前
|
机器学习/深度学习 人工智能 算法
深度探索数据聚合算法:提高文档管理软件整理效率的秘诀
在这个数字时代,文档管理软件成为了我们日常生活和工作中的强力伙伴。然而,随着文档数量的爆炸增长,文档的整理和分类变得越来越令人头疼。幸运的是,有了新一代的数据聚合算法,我们能够轻松摆脱繁琐的整理工作,使文档管理变得轻松愉快。接下来,让我们深入探讨一下数据聚合算法如何提高文档管理软件中的文档整理效率。
160 0
|
12天前
|
数据采集 算法 数据可视化
MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究
MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究
|
2月前
|
算法 数据可视化 计算机视觉
使用Python实现图像处理中的边缘检测算法
图像处理中的边缘检测是计算机视觉和图像识别领域的重要技术之一。本文将介绍如何利用Python语言实现常见的边缘检测算法,包括Sobel、Canny等,并结合实例演示其在图像处理中的应用。
|
5月前
|
存储 算法 搜索推荐
PACS/RIS医学影像管理系统源码 提供先进图像处理和算法
PACS/RIS医学影像管理系统源码 提供先进图像处理和算法
39 0
|
8月前
|
机器学习/深度学习 算法 C语言
FPGA图像处理之边缘检测算法的实现
边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括(i)深度上的不连续、(ii)表面方向不连续、(iii)物质属性变化和(iv)场景照明变化。边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。
FPGA图像处理之边缘检测算法的实现
|
8月前
|
算法 计算机视觉 异构计算
FPGA图像处理之rgbtogray算法的实现
Ycbcrr或Y'CbCr有的时候会被写作:YCBCR或是Y'CBCR,是色彩空间的一种,通常会用于影片中的影像连续处理,或是数字摄影系统中。Y'为颜色的亮度(luma)成分、而CB和CR则为蓝色和红色的浓度偏移量成份。Y'和Y是不同的,而Y就是所谓的流明(luminance),表示光的浓度且为非线性,使用伽马修正(gamma correction)编码处理。
|
10月前
|
算法 计算机视觉
差分进化算法在图像处理中的应用研究(Matlab代码实现)
差分进化算法在图像处理中的应用研究(Matlab代码实现)
|
1天前
|
算法
【免费】基于ADMM算法的多微网电能交互分布式运行策略(matlab代码)
【免费】基于ADMM算法的多微网电能交互分布式运行策略(matlab代码)
|
1天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于有序抖动块截断编码的水印嵌入和提取算法matlab仿真
这是一个关于数字图像水印嵌入的算法介绍。使用MATLAB2022a,该算法基于DOTC,结合抖动和量化误差隐藏,确保水印的鲁棒性和隐蔽性。图像被分为N*N块,根据水印信号进行二值化处理,通过调整重建电平的奇偶性嵌入水印。水印提取是嵌入过程的逆操作,通过重建电平恢复隐藏的水印比特。提供的代码片段展示了从块处理、水印嵌入到噪声攻击模拟及水印提取的过程,还包括PSNR和NC的计算,用于评估水印在不同噪声水平下的性能。