聚类的一个常用场景是对图像进行分割.
图像分割就是利用图像自身的信息, 比如颜色、纹理、形状等特征进行划分, 将图像分割成不同的区域, 划分出来的每个区域就相当于是对图像中的像素进行了聚类. 单个区域内的像素之间的相似度大, 不同区域间的像素差异性大. 这个特性正好符合聚类的特性, 所以可以把图像分割看成是将图像中的信息进行聚类.
本例通过使用python图像处理标准库PIL获取图片每个像素的三个通道值数据并进行标准化, 再使用K-Means算法对数据进行聚类, 最后输出聚类后重构的图像.
首先我们选取一张图片, 为了节省时间我使用了最近很人气旺的漫威超级反派图片(Thanos.jpg), 大小为58.4kb.
之后我们定义一个函数, 函数的主要功能为 加载图片数据并获取图像的尺寸和通道数,然后基于图像中每个通道的数值进行数据规范化, 最后返回规范化后的特征值、图像宽度和高度. 由于图片的颜色通道值为0~255, 我们使用值 (n + 1) / 256.0 使规范化数据处于(0, 1] 区间内, 用以之后的聚类分析.
相关代码:
# 定义函数: 加载图像, 并进行规范化
def load_data(filepath):
f = open(filepath, 'rb')
data = []
# 得到图像的像素值
img = image.open(f)
# 得到图像的尺寸
width, height = img.size
for i in range(width):
for j in range(height):
# 得到点(i, j)的三个通道值
c1, c2, c3 = img.getpixel((i, j))
data.append([(c1+1)/256.0, (c2+1)/256.0, (c3+1)/256.0])
f.close()
return np.mat(data), width, height
调用函数, 传入图片路径, 返回规范化数据n_data, 宽度width, 高度height. 然后使用K-Means算法进行聚类, 设置聚类中心数为4, 得到包含4个类别的label数组, 然后利用已知的width和height把聚类结果转化为图像尺寸的矩阵.
相关代码:
# 加载图像,得到规范化的结果n_data和图像尺寸
n_data, width, height = load_data('./Thanos.jpg')
# 用K-Means对图像进行聚类
kmeans = KMeans(n_clusters=4)
label = kmeans.fit_predict(n_data)
# 将图像聚类结果,转化成图像尺寸的矩阵
label = label.reshape([width, height])
使用skimage 中的label2rgb 函数来将 label 分类标识转化为颜色数值, 因为我们的颜色值范围是[0, 255], 所以还需要乘以 255 进行转化, 再转化为整数类型. 最后使用fromarray函数生成图片.
代码中使用了label_color.transpose(1, 0, 2), 是因为之前生成的图片是颠倒的, 我们通过transpose函数使矩阵的第一维(0)和第二维(1)进行了互换.
相关代码:
# 将聚类标识矩阵转化为不同颜色的矩阵
label_color = (color.label2rgb(label) * 255).astype(np.uint8)
label_color = label_color.transpose(1, 0, 2)
images = image.fromarray(label_color)
images.save('mark_color2.jpg')
得到图片:

从图中可以看出, 图片中的颜色区域被分成了四个类别, 分别用红、黄、蓝、紫四种颜色表示.
因为图片是通过K-Means做的聚类可视化, 我们也可以将每个像素点的RGB值设置为其对应簇质心点的RGB值, 也就是簇内的点的特征均为质心点的特征. 因为聚类前使用的标准化后的数据, 我们通过int(c1 * 256) - 1公式求得实际通道值, 代码实现如下:
# 创建个新图像img,用来保存图像聚类压缩后的结果
img = image.new('RGB', (width, height))
for x in range(width):
for y in range(height):
# 提取 (x, y) 点对应的聚类中心的通道值
c1 = kmeans.cluster_centers_[label[x, y], 0]
c2 = kmeans.cluster_centers_[label[x, y], 1]
c3 = kmeans.cluster_centers_[label[x, y], 2]
img.putpixel((x, y), (int(c1*256)-1, int(c2*256)-1, int(c3*256)-1))
img.save('new_4.jpg')
上图中对应的颜色即为四个簇质心点的颜色.
我们可以设置不同的聚类数来得到相应的切割图像, 当设置n_clusters=16 时, 以上两种方法对应的图如下:
可以看出, 随聚类类别的增加, 各像素之间的差异划分增强, 图像被切割得越细密, 且簇质心色图越来越清晰, 愈加接近于原图像.
本文介绍了如何运用K-Means算法进行图像分割。通过加载和规范化图像数据,利用K-Means进行聚类,最终将聚类结果转化为彩色图像,展示不同颜色代表的不同区域。通过调整聚类数量,可以实现对图像更细致的分割。
1673

被折叠的 条评论
为什么被折叠?



