關於K聚類,我曾經在一篇博客中提到過,這里簡單的做個回顧。
KMeans的步驟以及其他的聚類算法
K-均值是因為它可以發現k個不同的簇,且每個簇的中心采用簇中所含值的均值計算
其他聚類算法:二分K-均值
講解一下步驟,其實就是說明一下偽代碼
隨機選擇k個點作為起始質心
當任意一個點的簇分配結果發生改變時
對數據集中的每個數據點
對每個質心
計算質心與數據點之間的距離
將數據分配到距其最近的簇
對每一個簇,計算簇中所有點的均值並將均值作為質心
OpenCV中使用cv2.kmeans()對數據進行分類
理解函數的參數
輸入參數:cv2.kmeans(data,K, bestLabels,criteria,attempt,flags)
1. data:應該是np.float32類型的數據,每個特征應該放在一列。
2. K:聚類的最終數目
3. criteria:終止迭代的條件。當條件滿足,算法的迭代終止。它應該是一個含有3個成員的元組,它們是(type,max_iter, epsilon):
type終止的類型:有如下三種選擇:
- cv2.TERM_CRITERIA_EPS 只有精確度epslion滿足時停止迭代
- cv2.TERM_CRITERIA_MAX_ITER 當迭代次數超過閾值時停止迭代
– cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER 上面的任何一個條件滿足時停止迭代
max_iter:最大迭代次數
epsilon:精確度閾值
4. attempts:使用不同的起始標記來執行算法的次數。算法會返回緊密度最好的標記。緊密度也會作為輸出被返回
5. flags:用來設置如何選擇起始中心。通常我們有兩個選擇:cv2.KMEANS_PP_CENTERS和 cv2.KMEANS_RANDOM_CENTERS。
輸出參數:
1. compactness:緊密度返回每個點到相應中心的距離的平方和
2. labels:標志數組,每個成員被標記為0,1等
3. centers:有聚類的中心組成的數組
僅有一個特征的數據
假設我們有一組數據,每個數據只有一個特征。例如前面的T恤問題,我們只用身高來決定T恤的大小。我們來產生一些隨機數據,並使用Matplotlib
# 隨機在25~100之間產生25個值
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()
我們使用KMeans函數。先設置好終止條件。10次迭代或者精確度epsilon=1.0
# 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)
把數據分成兩組
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()
含有多個特征的數據
T恤我們只考慮了身高,現在將體重也考慮進去,也就是兩個特征。在本例中我們的測試數據是50x2的向量,其中包含50個人的身高和體重。第一列對應身高,第二列對應體重。如下圖所示:
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() # 迭代次數為10次,精確度為1.0 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()
顏色量化
顏色量化就是減少圖片中顏色數目的一個過程,其原因是為了減少內存消耗。現在有3個特征:R,G,B,所以我們需要把圖片數據變形成Mx3(M是圖片中像素點的數目)的向量。聚類完成后,我們用聚類中心值替換與其同簇的像素值,這樣結果圖片就只含指定數目的顏色了。
分別取K=2、4、8
import numpy as np import cv2 img = cv2.imread('home.jpg') Z = img.reshape((-1,3)) # convert to np.float32 Z = np.float32(Z) # define criteria, number of clusters(K) and apply kmeans() criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) K = 2 ret,label,center=cv2.kmeans(Z,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS) # Now convert back into uint8, and make original image center = np.uint8(center) res = center[label.flatten()] res2 = res.reshape((img.shape)) cv2.imshow('K=2',res2) cv2.imwrite('K=2.png', res2) cv2.waitKey(0) cv2.destroyAllWindows()