K-means聚类实现图片分割
K均值聚类是非监督学习,可以将图像分割成若干部分,方法是把具相似特征的数据点聚类或分组到一起。非监督学习的特点是,无需使用标签数据,算法会识别出多组数据的模式和相似之处。所以你可以给 k 均值任意一个无标签数据集,比如图像的像素值,然后让 k 均值将这个数据集分解成 k 簇,其中 k 是变量 ,你可以选择该变量的值。若我们设K为2,那么图像就会被分割成
两个颜色不同的部分,若为3,就被分成3个颜色不同的部分。
例如如下的彩虹的图片:

这张图尺寸较小,仅占34×34像素的空间,并作为彩虹的一部分存在。我选择使用k均值聚类算法来将这张图像按照颜色特征划分为三个不同的簇群。首先已知该图像中每个像素对应一个RGB颜色数值。可以将各像素的RGB值视为RGB颜色空间中的数据点进行绘制。

这些轴分别代表 R、G 和 B 通道的数值,在该轴上具有最高数值的位置对应于纯白色。引导k均值算法对图像数据进行聚类时,k均值算法会分析这些像素的RGB数值,并随机选取三个RGB基准点作为初始聚类中心。并将这三个基准点定义为聚类中心A、B和C,随后k均值算法会根据数据特征逐步优化并确定最终的聚类结果。

然后对变量K进行赋值,在每个像素上将各像素值分配给与其最近的中心点所属的簇中,在左侧所有这些像素归为A簇;右侧相近区域划归B簇;底部区域则分配至C簇。系统会分别计算这三个区域所有RGB分量的实际平均数值作为均质中心点位置。接着系统会重新确定中心点位置,并不断重复上述过程直至达到收敛状态。收敛条件设定为当迭代次数达到设定阈值时会自动终止;或者观察每次迭代后各中心点移动距离的变化情况来决定是否终止迭代过程。如果发现各中心点之间的移动距离小于预设的小量(例如某次迭代仅移动一象素宽度),那么算法就会认为达到了收敛状态并终止运行。
加载图片
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均值在图像分割中的作用十分明显,希望可以熟练应用~
