K-Means聚類概念:
K-Means聚類是最常用的聚類算法,最初起源于信號處理,其目標是將數據點劃分為K個類簇, 找到每個簇的中心并使其度量最小化。 該算法的最大優點是簡單、便于理解,運算速度較快,缺點是只能應用于連續型數據,并且要 在聚類前指定聚集的類簇數。 k-means算法是一種原型聚類算法。
K-Means聚類分析流程:
第一步,確定K值,即將數據集聚集成K個類簇或小組。
第二步,從數據集中隨機選擇K個數據點作為質心(Centroid)或數據中心。
第三步,分別計算每個點到每個質心之間的距離,并將每個點劃分到離最近質心的小組。
第四步,當每個質心都聚集了一些點后,重新定義算法選出新的質心。(對于每個簇,計 算其均值,即得到新的k個質心點)
第五步,迭代執行第三步到第四步,直到迭代終止條件滿足為止(分類結果不再變化)
舉個例子:
起始情況:
第一步,確定K值,即將數據集聚集成K個類簇或小組。 ----這里我們選K=2
第二步,從數據集中隨機選擇K個數據點作為質心(Centroid)或數 據中心。----假設我們選擇P1和P2作為初始的質心
第三步,分別計算每個點到每個質心之間的距離,并將每個點劃分 到離最近質心的小組。 ----計算P3到P1的距離:√10 = 3.16; ----計算P3到P2的距離:√((3-1)2+(1-2)2 = √5 = 2.24; ----所以P3離P2更近,P3就加入P2的簇。同理,P4、P5、P6;
P3到P6都跟P2更近,所以第一次分組的結果是:
? 組A:P1
? 組B:P2、P3、P4、P5、P6
按照上一次的方法選出兩個新的虛擬質心: —P哥1(1.33,1), P哥2(9,8.33)。
第三次計算點到質心的距離:
— 這時可以看到P1、P2、P3離P哥1更近,P4、 P5、P6離P哥2更近。
所以第三次分組的結果是:
? 組A:P1、P2、P3
? 組B:P4、P5、P6
我們發現,這次分組的結果和上次沒有任何變化了,說 明已經收斂,聚類結束。
K-Means聚類應用:
在圖像處理中,通過K-Means聚類算法可以實現圖像分割、圖像聚類、圖像識別等操作。 我們通過K-Means可以將這些像素點聚類成K個簇,然后使用每個簇內的質心點來替換簇內所有 的像素點,這樣就能實現在不改變分辨率的情況下量化壓縮圖像顏色,實現圖像顏色層級分割。
K-Means聚類優缺點:
優點:
1.是解決聚類問題的一種經典算法,簡單、快速
2.對處理大數據集,該算法保持可伸縮性和高效率
3.當結果簇是密集的,它的效果較好
缺點:
1.在簇的平均值可被定義的情況下才能使用,可能不適用于某些應用
2.必須事先給出k(要生成的簇的數目),而且對初值敏感,對于不同的初始值,可能會導致不同結果。
3.不適合于發現非凸形狀的簇或者大小差別很大的簇
4.對躁聲和孤立點數據敏感
算法實現
簡單的灰色圖像聚類:
'''
在OpenCV中,Kmeans()函數原型如下所示:
compactness, Labels, centers = kmeans(data, K, bestLabels, criteria, attempts, flags[, centers])返回值:compactness:緊密度,返回每個點到相應重心的距離的平方和labels:結果標記,每個成員被標記為分組的序號,如 0,1,2,3,4...等centers:由聚類的中心組成的數組輸入值:data表示聚類數據,最好是np.flloat32類型的N維點集K表示聚類類簇數bestLabels表示輸出的整數數組,用于存儲每個樣本的聚類標簽索引criteria表示算法終止條件,即最大迭代次數或所需精度。在某些迭代中,一旦每個簇中心的移動小于criteria.epsilon,算法就會停止attempts表示重復試驗kmeans算法的次數,算法返回產生最佳緊湊性的標簽flags表示初始中心的選擇,兩種方法是cv2.KMEANS_PP_CENTERS ;和cv2.KMEANS_RANDOM_CENTERScenters表示集群中心的輸出矩陣,每個集群中心為一行數據
'''import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始圖像灰度顏色
img = cv2.imread('lenna.png', 0)
print (img.shape)#獲取圖像高度、寬度
rows, cols = img.shape[:]#圖像二維像素轉換為一維
data = img.reshape((rows * cols, 1))
data = np.float32(data)#定義終止條件 (type,max_iter,epsilon)
criteria = (cv2.TERM_CRITERIA_EPS +cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)#每次隨機選擇初始中心
flags = cv2.KMEANS_RANDOM_CENTERS#K-Means聚類 聚集成4類
compactness, labels, centers = cv2.kmeans(data, 4, None, criteria, 10, flags)#生成最終圖像
dst = labels.reshape((img.shape[0], img.shape[1]))#用來正常顯示中文標簽
plt.rcParams['font.sans-serif']=['SimHei']#顯示圖像
titles = [u'原始圖像', u'聚類圖像']
images = [img, dst]
for i in range(2): plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray'), plt.title(titles[i]) plt.xticks([]),plt.yticks([])
plt.show()
結果展示:
彩色圖像不同類數的聚類效果對比:
import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始圖像
img = cv2.imread('lenna.png')
print (img.shape)#圖像二維像素轉換為一維
data = img.reshape((-1,3))
#先前我們不知道z的shape屬性是多少,但是想讓z變成只有三列
data = np.float32(data)
#轉換為float32位#定義終止條件(type,max_iter,epsilon)
criteria = (cv2.TERM_CRITERIA_EPS +cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)#隨機選定初始中心
flags = cv2.KMEANS_RANDOM_CENTERS#K-Means聚類 聚集成2類
compactness, labels2, centers2 = cv2.kmeans(data, 2, None, criteria, 10, flags)#K-Means聚類 聚集成4類
compactness, labels4, centers4 = cv2.kmeans(data, 4, None, criteria, 10, flags)#K-Means聚類 聚集成8類
compactness, labels8, centers8 = cv2.kmeans(data, 8, None, criteria, 10, flags)#K-Means聚類 聚集成16類
compactness, labels16, centers16 = cv2.kmeans(data, 16, None, criteria, 10, flags)#K-Means聚類 聚集成64類
compactness, labels64, centers64 = cv2.kmeans(data, 64, None, criteria, 10, flags)#圖像轉換回uint8二維類型
centers2 = np.uint8(centers2)
res = centers2[labels2.flatten()]
dst2 = res.reshape((img.shape))centers4 = np.uint8(centers4)
res = centers4[labels4.flatten()]
dst4 = res.reshape((img.shape))centers8 = np.uint8(centers8)
res = centers8[labels8.flatten()]
dst8 = res.reshape((img.shape))centers16 = np.uint8(centers16)
res = centers16[labels16.flatten()]
dst16 = res.reshape((img.shape))centers64 = np.uint8(centers64)
res = centers64[labels64.flatten()]
dst64 = res.reshape((img.shape))#圖像轉換為RGB顯示
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
dst2 = cv2.cvtColor(dst2, cv2.COLOR_BGR2RGB)
dst4 = cv2.cvtColor(dst4, cv2.COLOR_BGR2RGB)
dst8 = cv2.cvtColor(dst8, cv2.COLOR_BGR2RGB)
dst16 = cv2.cvtColor(dst16, cv2.COLOR_BGR2RGB)
dst64 = cv2.cvtColor(dst64, cv2.COLOR_BGR2RGB)#用來正常顯示中文標簽
plt.rcParams['font.sans-serif']=['SimHei']#顯示圖像
titles = [u'原始圖像', u'聚類圖像 K=2', u'聚類圖像 K=4',u'聚類圖像 K=8', u'聚類圖像 K=16', u'聚類圖像 K=64']
images = [img, dst2, dst4, dst8, dst16, dst64]
for i in range(6): plt.subplot(2,3,i+1), plt.imshow(images[i], 'gray'), plt.title(titles[i]) plt.xticks([]),plt.yticks([]) #xticks()函數可以用來設置使x軸上ticks隱藏,即將空數組賦予它,則沒有tick會顯示在x軸上
plt.show()
輸出結果: