在上一篇(【Machine Learning】K-means算法及優化)中,我們介紹了K-means算法的基本原理及其優化的方向。opencv中也提供了K-means算法的接口,這裏主要介紹一下如何在python+opencv的環境下,使用k-means接口(C++的接口形式相似)。
python版本的k-means算法接口,即cv2.kmeans()。
輸入參數:
-samples: 類型必須是np.float32型,每個特徵應該放置在單獨的一個列中;
-nclusters(K):最終要確定的類別數;
-criteria:迭代終止的條件,3通道參數,分別是(type, max_iter, epsilon):
- 3.a -type: 終止條件的類型,有如下3個flag: cv2.TERM_CRITERIA_EPS(精度滿足epsilon時,終止迭代);cv2.TERM_CRITERIA_MAX_ITER(最大迭代次數達到後終止);cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER(二者滿足其一即可終止迭代);
- 3.b - max_iter:最大迭代次數;
- 3.c - epsilon:精度;
-attempts: 算法使用不同初始化標識執行的次數,改算法返回最佳集聚性的標籤,該值作爲輸出值返回;
-flags:用於指定如何初始化聚類中心,通常有兩種方式:cv2.KMEANS_PP_CENTERS(初始的K個點)和cv2.KMEANS_RANDOM_CENTERS(隨機產生的K個點)。
輸出參數:
-compactness: 集聚性,指每個點到相應聚類中心點的距離平方的和;
-labels:聚類的標識序列(‘0’, ‘1’......);
-centers:聚類中心的序列。
1、一維特徵的聚類
首先,使用如下代碼,創建一個隨機數組序列,並繪製其分佈直方圖:
import numpy as np
import cv2
from matplotlib import pyplot as plt
x = np.random.randint(25,100,25)
y = np.random.randint(175,255,25)
z = np.hstack((x,y))
z = z.reshape((50,1))
z = np.float32(z)
plt.hist(z,256,[0,256]),plt.show()
這裏,調用K-means方程,首先我們要設定好criteria,這裏最大迭代次數設定爲10,誤差設定爲1.0,且終止條件指定爲不管達到誤差還是達到最大迭代次數,都將終止迭代。同時,指定將以上數據聚類爲2類。具體代碼如下:
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS
# Apply KMeans
compactness,labels,centers = cv2.kmeans(z,2,None,criteria,10,flags)
這裏返回了compactness, labels and centers.這裏,我們得到64和210兩個中心點(每次運行可能不太一樣,因爲數據是隨機出來的)。labels的大小和輸入數據的大小相同,每個數據都將根據他們所屬類的中心被標記爲‘0’,‘1’,‘2’ 等等。下面將根據他們的標識,將數據按類分離開:
A = z[labels==0]
B = z[labels==1]
下面將A類用紅色繪製,B類用藍色繪製,他們的中心均用黃色繪製:
# Now plot 'A' in red, 'B' in blue, 'centers' in yellow
plt.hist(A,256,[0,256],color = 'r')
plt.hist(B,256,[0,256],color = 'b')
plt.hist(centers,32,[0,256],color = 'y')
plt.show()
可得到如下結果:
2、多維特徵的聚類
下面考慮二維特徵的聚類,按照上面類似的思路,生成數據,進行聚類,並繪製聚類結果。代碼如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
X = np.random.randint(25,50,(25,2))
Y = np.random.randint(60,85,(25,2))
Z = np.vstack((X,Y))
# convert to np.float32
Z = np.float32(Z)
# define criteria and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
ret,label,center=cv2.kmeans(Z,2,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# Now separate the data, Note the flatten()
A = Z[label.ravel()==0]
B = Z[label.ravel()==1]
# Plot the data
plt.scatter(A[:,0],A[:,1])
plt.scatter(B[:,0],B[:,1],c = 'r')
plt.scatter(center[:,0],center[:,1],s = 80,c = 'y', marker = 's')
plt.xlabel('Height'),plt.ylabel('Weight')
plt.show()
聚類結果如下:
2017.11.17