K均值聚类是非监督学习,可以将图像分割成若干部分,方法是把具相似特征的数据点聚类或分组到一起。非监督学习的特点是,无需使用标签数据,算法会识别出多组数据的模式和相似之处。所以你可以给 k 均值任意一个无标签数据集,比如图像的像素值,然后让 k 均值将这个数据集分解成 k 簇,其中 k 是变量 ,你可以选择该变量的值。若我们设K为2,那么图像就会被分割成
两个颜色不同的部分,若为3,就被分成3个颜色不同的部分。
例如如下的彩虹的图片:
这张图很小, 只有 34 乘 34 像素 ,是彩虹的一部分,我要用 k 均值,根据颜色将这张图分为三簇,首先 我们知道这张图里的每个像素都有一个 RGB 值。
事实上 ,我们可以将各像素值当作 RGB 颜色空间的数据点绘制出来。
这些轴分别对应 R、G 和 B 的值,最高的位置对应的是白色,如果我让 k 均值将这些图像数据分成三簇,那么 k 均值就会观察这些像素值,随机猜测三个 RGB 点 ,将数据分成三簇,我们将这三个点称为中心点 A、B 和 C,然后 k 均值会进行赋值。
然后对K赋值,把各像素值赋给最近的中心点所在的簇,所以在左边的所有这些像素都属于 A 簇;靠近右边的则归入 B 簇;底部的则归入 C 簇。K均值会分别取这三个区域的所有RGB值的实际平均值,也就是均值。然后K均值会更新中心点的位置,换成这个均值所处的位置。三个簇不断重复这个过程,相互拉扯,从而形成新的数据中心,不断调整,直到收敛。收敛的定义是通常算法会在一定迭代次数后收敛 ,比如 10 次;或根据每次迭代后中心点的移动幅度来确定是否要收敛。如果中心点的移动幅度小于某个较小的值,比如某次迭代时,移动幅度只有一像素,那么中心点就收敛了 算法会终止。
接下来,用一段代码实例来看看K均值聚类:
加载图片
import numpy as np
import matplotlib.pyplot as plt
import cv2
%matplotlib inline
# Read in the image
## TODO: Check out the images directory to see other images you can work with
# And select one!
image = cv2.imread('images/monarch.jpg')
# Change color to RGB (from BGR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
K均值数据准备
# Reshape image into a 2D array of pixels and 3 color values (RGB)
pixel_vals = image.reshape((-1,3))
# Convert to float type
pixel_vals = np.float32(pixel_vals)
实现K均值聚类
## TODO: Visualize one segment, try to find which is the leaves, background, etc!
plt.imshow(labels_reshape==0, cmap='gray')
# mask an image segment by cluster
cluster = 0 # the first cluster
masked_image = np.copy(image)
# turn the mask green!
masked_image[labels_reshape == cluster] = [0, 255, 0]
plt.imshow(masked_image)
# define stopping criteria
# you can change the number of max iterations for faster convergence!
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
## TODO: Select a value for k
# then perform k-means clustering
k = 3
retval, labels, centers = cv2.kmeans(pixel_vals, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# convert data into 8-bit values
centers = np.uint8(centers)
segmented_data = centers[labels.flatten()]
# reshape data into the original image dimensions
segmented_image = segmented_data.reshape((image.shape))
labels_reshape = labels.reshape(image.shape[0], image.shape[1])
plt.imshow(segmented_image)
K均值在图像分割中的作用十分明显,希望可以熟练应用~