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
三:關鍵代碼解析
歐幾裡德距離計算方法如下:
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
fuzzyforpixels[i][j] = (fuzzyforpixels[i][j] - min) / (max - min);
sum += fuzzyforpixels[i][j];
//makes it so that the sum of all values is 1
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[] 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++)
jk += math.pow(fuzzyforpixels[i][j], this.fuzzy) * math.pow(this.calculateeuclideandistance(points.get(i), clusters.get(j)), 2);
return jk;
private void recalculateclustermembershipvalues()
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
fuzzyforpixels[i][j] = (fuzzyforpixels[i][j] - min) / (max - min);
sum += fuzzyforpixels[i][j];
//makes it so that the sum of all values is 1
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- 存儲圖像像素點對象
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) {
public double gety() {
return y;
public void sety(double 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資訊對象
public class clustercentroid {
private double redsum;
private double greensum;
private double bluesum;
private double membershipsum;
public clustercentroid(double x, double y, int color)
this.originalpixelcolor = color;
this.pixelcolor = color;
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;
算法調用:
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,運作時間比較長。