OpenCV 中的 K-Means 聚类

目标

  • 学习在 OpenCV 中使用 cv.kmeans() 函数进行数据聚类

了解参数

输入参数

  1. samples : 应该是 np.float32 数据类型,并且每个功能都应该放在一个列中。
  2. nclusters(K) : 结束时所需的集群数量
  3. criteria : 迭代终止标准。满足此条件时,算法迭代停止。实际上,这是一个有 3 个元素的 tuple。它们是( type, max_iter, epsilon ):
    1. 终止标准的类型。 它有 3 个标志如下:
      • cv.TERM_CRITERIA_EPS - 如果达到指定的精度 epsilon,则停止算法迭代。
      • cv.TERM_CRITERIA_MAX_ITER - 在指定的迭代次数 max_iter 之后停止算法。
      • cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER - 当满足上述任何条件时停止迭代。
    2. max_iter - 一个指定最大迭代次数的整数。
    3. epsilon - 要求的精度
  4. attempts : 用于指定使用不同初始标记执行算法的次数的标志。算法返回产生最佳紧凑性的标签。该紧凑性作为输出返回。
  5. flags : 该标志用于指定初始中心的采用方式。通常会使用两个标志: cv.KMEANS_PP_CENTERScv.KMEANS_RANDOM_CENTERS

输出参数

  1. compactness : 每个点到其相应中心的平方距离之和。
  2. labels : 标签数组(与前一篇文章中的’代码’相同),其中每个元素都标记为’0’,’1’…..
  3. centers : 聚类中心数组

现在我们将通过三个例子看到如何应用 K-Means 算法。

1. 只有一个特征的数据

考虑一下,你有一组只有一个特征的数据,即一维。 例如,我们可以采用我们的 T 恤问题,只使用人的高度来决定 T 恤的大小。

因此,我们首先创建数据并在 Matplotlib 中绘制它。

  1. import numpy as np
  2. import cv2 as cv
  3. from matplotlib import pyplot as plt
  4. x = np.random.randint(25,100,25)
  5. y = np.random.randint(175,255,25)
  6. z = np.hstack((x,y))
  7. z = z.reshape((50,1))
  8. z = np.float32(z)
  9. plt.hist(z,256,[0,256]),plt.show()

所以我们有’z’,这是一个大小为 50 的数组,值范围从 0 到 255。我已经将’z’ 重塑为列向量。当存在多个特征时,它将更有用。然后我将类型数据转为 np.float32 。

我们得到以下图片:

OpenCV 中的 K-Means 聚类 - 图1
图像

现在我们应用 KMeans 功能。在此之前,我们需要指定标准。我的标准是,每当运行 10 次迭代算法或达到 epsilon = 1.0 的精度时,就停止算法并返回答案。

  1. # Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
  2. criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
  3. # Set flags (Just to avoid line break in the code)
  4. flags = cv.KMEANS_RANDOM_CENTERS
  5. # Apply KMeans
  6. compactness,labels,centers = cv.kmeans(z,2,None,criteria,10,flags)

这为我们提供了紧凑性、标签和中心。 在这个例子下,我得到的中心为 60 和 207。标签将具有与测试数据相同的大小,其中每个数据将被标记为“0”,“1”,“2”等,具体取决于它们的中心。现在我们根据标签将数据拆分到不同的集群。

  1. A = z[labels==0]
  2. B = z[labels==1]

现在我们用红色绘制 A,用蓝色绘制 B,用黄色绘制中心。

  1. # Now plot 'A' in red, 'B' in blue, 'centers' in yellow
  2. plt.hist(A,256,[0,256],color = 'r')
  3. plt.hist(B,256,[0,256],color = 'b')
  4. plt.hist(centers,32,[0,256],color = 'y')
  5. plt.show()

以下是我们得到的输出:

OpenCV 中的 K-Means 聚类 - 图2
图像

2.具有多个特征的数据

在前面的例子中,我们只采取了 T 恤问题的高度。在这里,我们将采用高度和重量,即两个特征。

请记住,在前一种情况下,我们将数据制作为单个列向量。 每个特征排列在一列中,而每行对应一个输入测试样本。

举个例子,在本例中,我们将测试数据集的大小设为 50x2 ,这是 50 个人的高度和体重。第一列对应着 50 个人的高度,第二列对应着他们的重量。第一行包含两个元素,其中第一个元素是第一人的高度,第二个元素是他的重量。类似地,剩余的行对应于其他人的高度和重量。查看下图:

OpenCV 中的 K-Means 聚类 - 图3
图像
现在我直接转到代码: python import numpy as np import cv2 as cv 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 = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0) ret,label,center=cv.kmeans(Z,2,None,criteria,10,cv.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() 以下是我们得到的输出:
OpenCV 中的 K-Means 聚类 - 图4
图像

3.色彩量化

色彩量化是减少图像中颜色数量的过程。这样做的一个原因是减少内存。有时,某些设备可能具有限制,使得它只能产生有限数量的颜色。在那些情况下,也执行色彩量化。这里我们使用 k-means 聚类进行色彩量化。

这里没有什么新的概念需要解释。有 3 个特征,叫做 R、G、B。因此,我们需要将图像重塑为 Mx3 大小的数组(M 是图像中的像素数)。在聚类之后,我们将中心值(它也是 R、G、B)应用于所有像素,这样得到的图像将具有指定数量的颜色。

  1. import numpy as np
  2. import cv2 as cv
  3. img = cv.imread('home.jpg')
  4. Z = img.reshape((-1,3))
  5. # convert to np.float32
  6. Z = np.float32(Z)
  7. # define criteria, number of clusters(K) and apply kmeans()
  8. criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
  9. K = 8
  10. ret,label,center=cv.kmeans(Z,K,None,criteria,10,cv.KMEANS_RANDOM_CENTERS)
  11. # Now convert back into uint8, and make original image
  12. center = np.uint8(center)
  13. res = center[label.flatten()]
  14. res2 = res.reshape((img.shape))
  15. cv.imshow('res2',res2)
  16. cv.waitKey(0)
  17. cv.destroyAllWindows()

对于 K = 8,请参见下面的结果:

OpenCV 中的 K-Means 聚类 - 图5
图像

额外资源

练习