影象基本變換---KMeans聚類演算法
阿新 • • 發佈:2018-12-31
本文將詳細介紹K-Means均值聚類的演算法及實現。
聚類是一個將資料集中在某些方面相似的資料成員進行分類組織的過程。K均值聚類是最著名的劃分聚類演算法,由於簡潔和效率使得他成為所有聚類演算法中最廣泛使用的。給定一個數據點集合和需要的聚類數目k,k由使用者指定,k均值演算法根據某個距離函式反覆把資料分入k個聚類中。
演算法過程:
1,初始化聚類數目K,並任意選擇K個初始化均值ui。
2,迭代影象中每個畫素f(x,y),判斷其距離哪一個聚類均值最近,即|f(x,y)-ui|最小,則將該畫素劃分歸屬為對應的聚類i。
3,完成一次迭代後,計算每一個聚類的新的均值ui,均值公式不在重複。
4,重複步驟2-3,直到相鄰兩次迭代中每一個聚類的ui不在發生變化,則迭代結束。
5,得到最後的分類結果。
注:本文程式碼中採用的是對灰度影象進行KMeans聚類,然後還原彩色資訊。 下面 我們給出一份Win8 C#程式碼,另附一份Winform C#程式碼DEMO,在文章末尾連結 中。- ///
- /// KMeans Cluster process.
- ///
- /// The source image.
- /// Cluster threshould, from 2 to 255.
- ///
- public
- {
- if (src != null)
- {
- int w = src.PixelWidth;
- int h = src.PixelHeight;
- WriteableBitmap dstImage = new WriteableBitmap(w, h);
- byte
- byte[] tempMask = (byte[])temp.Clone();
- int b = 0, g = 0, r = 0;
- //定義灰度影象資訊儲存變數
- byte[] imageData = new byte[w * h];
- //定義聚類均值儲存變數(儲存每一個聚類的均值)
- double[] meanCluster = new double[k];
- //定義聚類標記變數(標記當前畫素屬於哪一類)
- int[] markCluster = new int[w * h];
- //定義聚類畫素和儲存變數(儲存每一類畫素值之和)
- double[] sumCluster = new double[k];
- //定義聚類畫素統計變數(儲存每一類畫素的數目)
- int []countCluster = new int[k];
- //定義聚類RGB分量儲存變數(儲存每一類的RGB三分量大小)
- int[] sumR = new int[k];
- int[] sumG = new int[k];
- int[] sumB = new int[k];
- //臨時變數
- int sum = 0;
- //迴圈控制變數
- bool s = true;
- double[] mJduge = new double[k];
- double tempV = 0;
- int cou = 0;
- //獲取灰度影象資訊
- for (int j = 0; j < h; j++)
- {
- for (int i = 0; i < w; i++)
- {
- b = tempMask[i * 4 + j * w * 4];
- g = tempMask[i * 4 + 1 + j * w * 4];
- r = tempMask[i * 4 + 2 + j * w * 4];
- imageData[i + j * w] = (byte)(b * 0.114 + g * 0.587 + r * 0.299);
- }
- }
- while (s)
- {
- sum = 0;
- //初始化聚類均值
- for (int i = 0; i < k; i++)
- {
- meanCluster[i] = i * 255.0 / (k - 1);
- }
- //計算聚類歸屬
- for (int i = 0; i < imageData.Length; i++)
- {
- tempV = Math.Abs((double)imageData[i] - meanCluster[0]);
- cou = 0;
- for (int j = 1; j < k; j++)
- {
- double t = Math.Abs((double)imageData[i] - meanCluster[j]);
- if (tempV > t)
- {
- tempV = t;
- cou = j;
- }
- }
- countCluster[cou]++;
- sumCluster[cou] += (double)imageData[i];
- markCluster[i] = cou;
- }
- //更新聚類均值
- for (int i = 0; i < k; i++)
- {
- meanCluster[i] = sumCluster[i] / (double)countCluster[i];
- sum += (int)(meanCluster[i] - mJduge[i]);
- mJduge[i] = meanCluster[i];
- }
- if (sum == 0)
- {
- s = false;
- }
- }
- //計算聚類RGB分量
- for (int j = 0; j < h; j++)
- {
- for (int i = 0; i < w; i++)
- {
- sumB[markCluster[i + j * w]] += tempMask[i * 4 + j * w * 4];
- sumG[markCluster[i + j * w]] += tempMask[i * 4 + 1 + j * w * 4];
- sumR[markCluster[i + j * w]] += tempMask[i * 4 + 2 + j * w * 4];
- }
- }
- for (int j = 0; j < h; j++)
- {
- for (int i = 0; i < w; i++)
- {
- temp[i * 4 + j * 4 * w] = (byte)(sumB[markCluster[i + j * w]] / countCluster[markCluster[i + j * w]]);
- temp[i * 4 + 1 + j * 4 * w] = (byte)(sumG[markCluster[i + j * w]] / countCluster[markCluster[i + j * w]]);
- temp[i * 4 + 2 + j * 4 * w] = (byte)(sumR[markCluster[i + j * w]] / countCluster[markCluster[i + j * w]]);
- }
- }
- Stream sTemp = dstImage.PixelBuffer.AsStream();
- sTemp.Seek(0, SeekOrigin.Begin);
- sTemp.Write(temp, 0, w * 4 * h);
- return dstImage;
- }
- else
- {
- return null;
- }
- }