Fuzzy C-Means聚合算法在圖像分割(segmentation)和圖像視覺處理中經常被用到聚合算法之java
一本文是徹底基於JAVA語言實現Fuzzy C-Means聚合算法,並能夠運用到圖像處理中實現簡c++
單的對象提取。算法
一:數學原理dom
在解釋數學原理以前,請先看看這個連接算是熱身吧ide
看不懂不要緊。個人解釋足夠詳細,小學畢業均可以學會,本人就是小學畢業。this
Fuzzy C-means算法主要是比較RGB空間的每一個像素值與Cluster中的每一箇中心點值,最終給url
每一個像素指派一個值(0~1之間)說明該像素更接近於哪裏Cluster的中心點,模糊規則是該像idea
素對全部cluster的值之和爲1。簡單的舉例:假設圖像中有三個聚類cluster1,cluster2,cluster3,spa
像素A對三個聚類的值分別爲a1, a2, a3, 根據模糊規則a1 + a2 + a3 = 1。更進一步,若是a1.net
最大,則該像素比較接近於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 pr = (p.getPixelColor() >> 16) & 0xff;
- int pg = (p.getPixelColor() >> 8) & 0xff;
- int pb = p.getPixelColor() & 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;
-
-
- double sumTerms = 0.0;
-
- for (int ck = 0; ck < this.clusters.size(); ck++)
- {
- sumTerms += top / calculateEuclideanDistance(this.points.get(h), this.clusters.get(ck));
-
- }
-
- 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);
-
- 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;
- }
-
- for (int j = 0; j < this.clusters.size(); j++)
- {
- fuzzyForPixels[i][j] = (fuzzyForPixels[i][j] - min) / (max - min);
- sum += fuzzyForPixels[i][j];
- }
-
- 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;
- }
-
- 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;
-
-
- private double numObj;
-
- public void setObj(double j) {
- this.numObj = j;
- }
-
- public double getObj() {
- return this.numObj;
- }
-
- private float fuzzy;
- private int numCluster;
-
- public BufferedImage getResultImage()
- {
- return this.processedImage;
- }
-
- public FuzzyCMeansProcessor(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>();
-
-
- 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;
-
-
- 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];
- }
- }
-
-
- 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);
- }
-
-
-
- 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();
- int col = (int)p.getY();
- index = row * width + col;
- outPixels[index] = this.clusters.get(i).getPixelColor();
- }
- }
- }
-
-
- setRGB( tempImage, 0, 0, width, height, outPixels );
- processedImage = tempImage;
- }
-
-
-
-
- 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;
-
-
- double sumTerms = 0.0;
-
- for (int ck = 0; ck < this.clusters.size(); ck++)
- {
- sumTerms += top / calculateEuclideanDistance(this.points.get(h), this.clusters.get(ck));
-
- }
-
- 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);
-
- 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;
- }
-
- for (int j = 0; j < this.clusters.size(); j++)
- {
- fuzzyForPixels[i][j] = (fuzzyForPixels[i][j] - min) / (max - min);
- sum += fuzzyForPixels[i][j];
- }
-
- 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;
- }
-
- p.setClusterIndex(newmax);
- };
- }
-
- private double calculateEuclideanDistance(ClusterPoint p, ClusterCentroid c)
- {
-
- int pr = (p.getPixelColor() >> 16) & 0xff;
- int pg = (p.getPixelColor() >> 8) & 0xff;
- int pb = p.getPixelColor() & 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 maxIterations = 20;
- double accuracy = 0.00001;
- 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,運行時間比較長。