下面用一个最常用的例子来给大家介绍K 值聚类。
话说有一个公司要生产一批新的T 恤。很明显他们要生产不同大小的T 恤来满足不同客客的要求。所以这个公司搜集了很多人的身高和体重信息,并把这些数据绘制在图上,如下所示:
肯定不能把每个大小的T 恤都生产出来,所以他们把所有的人分为三组,小,中,大,这三组要覆盖所有的人。我们可以使用K 值聚类的方法将所有人分为3 组,这个算法可以找到一个最好的分法并能覆盖所有人。如果不能覆盖全部人的话,公司就只能把这些人分为更多的组,可能是4 个或5 个甚至更多。如下图:
这个算法是一个迭代过程,我们会借助图片分步介绍它。考虑下面这组数据,你也可以把它当成T 恤问题来看,我们需要把他们分成两组。
第一步 随机选取两个重心点C1 和C2(有时可以选取数据中的两个点作为起始重心)。
第二步 计算每个点到这两个重心点的距离,如果距离C1 比较近,就标记为0,如果距离C2 比较近,就标记为1。(如果有更多的重心点,可以标记为2、3等)
在我们的例子中我们把属于0 的标记为红色,属于1 的标记为蓝色。我们就会得到下面这幅图。
第三步 重新计算所有蓝色点的重心和所有红色点的重心,并以这两个点更新重心点的位置。(图片只是为了演示说明而已,并不代表实际数据),重复步骤2,更新所有的点标记。我们就会得到下面的图:
继续迭代步骤2 和3,直到两个重心点的位置稳定下来。(当然也可以通过设置迭代次数,或者设置重心移动距离的阈值值来终止迭代。此时这些点到它们相应重心的距离之和最小。简单来说C1 到红色点的距离与C2 到蓝色点的距离之和最小。
最终结果如下图所示:
这就是对K 值聚类的一个直观解释。要想知道更多细节和数据,可以去一本关于机器学习的教科书或者参考更多资源中的链接。这只是K 值聚类的基础。现在对这个算法有很多改,比如,如何选取好的初始重心点,怎样加速迭代过程等。
参考资料:Supervised Learning in Machine Learning: Regression and Classification (DeepLearning.AI) | Coursera
cv2.kmeans()是OpenCV中用于执行k均值聚类的函数。它采用输入数据的特征向量,并将其分成指定数量的簇。
函数的语法如下:
retval, bestLabels, centers = cv2.kmeans(data, K, criteria, attempts, flags, centers)
参数说明:
返回值说明:
下面是一个使用cv2.kmeans()函数的简单示例:
import numpy as np
import cv2
# 准备数据
data = np.random.randint(0, 100, (100, 2)).astype(np.float32)
# 设置迭代停止的条件
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# 执行k均值聚类
retval, bestLabels, centers = cv2.kmeans(data, 2, None, 10, cv2.KMEANS_RANDOM_CENTERS)
# 打印结果
print("retval:", retval)
print("bestLabels:", bestLabels)
print("centers:", centers)
在上面的示例中,我们首先创建一个包含100个随机二维点的数据集。然后,我们设置了迭代停止的条件,指定要创建2个簇,并使用随机初始中心执行k均值聚类。最后,打印出返回的结果。
请注意,由于k均值聚类是一个迭代算法,结果可能因为初始中心的选择而有所不同。为了获得更好的结果,可以多次执行算法,并选择最佳结果。
颜色量化就是减少图片中颜色数目的一个过程。为什么要减少图片中的颜色呢?减少内存消耗,有些设备的资源有限,只能显示很少的颜色。在这种情况下就需要对颜色量化。我们使用K 值聚类的方法来进行颜色量化。
现在有3 个特征R,G,B。所以我们需要把图片数据变形成Mx3(M 是图片中像素点的数目)的向量。聚类完成后,我们用聚类中心值替换与其同组的像素值,这样结果图片就只含有指定数目的颜色了。下面是代码:
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 = 8
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('res2',res2)
cv2.waitKey(0)
cv2.destroyAllWindows()
下面是K=8 的结果: