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

三:关键代码解析

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

  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. return Math.sqrt(Math.pow((pr - cr), 2.0) + Math.pow((pg - cg), 2.0) + Math.pow((pb - cb), 2.0));
  12. }

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

  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. double top;
  8. top = calculateEuclideanDistance(this.points.get(h), this.clusters.get(c));
  9. if (top < 1.0) top = Eps;
  10. // sumTerms is the sum of distances from this data point to all clusters.
  11. double sumTerms = 0.0;
  12. for (int ck = 0; ck < this.clusters.size(); ck++)
  13. {
  14. sumTerms += top / calculateEuclideanDistance(this.points.get(h), this.clusters.get(ck));
  15. }
  16. // Then the membership value can be calculated as...
  17. fuzzyForPixels[h][c] = (double)(1.0 / Math.pow(sumTerms, (2 / (this.fuzzy - 1))));
  18. }
  19. };
  20. this.recalculateClusterMembershipValues();
  21. }

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

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

四:运行效果

五:算法源代码

FuzzyCMeansProcessor - 算法类

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

;

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