在上一篇(【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