Python图像处理丨基于K-Means聚类的图像区域分割
本文将详细阐述基于理论的图像分割方法,并使用K-Means聚类算法来完成图像分割和颜色层次处理的任务。作为基础性的文章,在此提供参考与帮助。
- 一、K-Means的核心原理
- 二、基于K-Means的灰度图像分割技术
- 三、K-Means在彩色图像空间差异性分析与处理中的应用
温馨提示:本部分内容均由杨秀璋查阅资料撰写的,请在授权范围内使用。感谢您的关注与支持!如需进一步交流,请随时私信我!共同进步吧!
该系列的所有代码资源托管于GitHub平台https://github.com/eastmountyxz/ImageProcessing-Python
一.K-Means聚类原理
第一部分知识主要来源于自己的新书《Python网络数据爬取及分析从入门到精通(分析篇)》以及之前的博客(二.Kmeans聚类数据分析)。
K-means聚类是一种广泛应用的聚类分析方法,在信号处理领域有较早的研究历史。该方法旨在通过建立合理的类别划分模型实现对样本数据的有效分组,并通过优化计算确定各组的核心代表点以达到最优分类效果。从理论体系来看,该算法的优势在于操作简便、易于理解和实现;但从应用效率的角度而言,则存在一定的局限性:首先,在实际应用中可能会遇到分类维度较高导致计算复杂度增大的挑战;其次,在面对非线性分布的数据特征时可能会出现分类效果不理想的状况;最后,在实际应用场景中还需要考虑如何合理设置初始迭代参数等问题。
下面是K-Means聚类算法的分析流程,步骤如下:
- 第一步, 确定聚类数量K, 即将数据集划分为K个簇或小组.
- 第二步, 从数据集中随机选取K个代表点作为初始质心(centroid).
- 第三步, 计算各数据点与各质心之间的距离, 并将每个数据点归入最近质心对应的簇中.
- 第四步, 当每个簇聚集完成后, 重新计算新的质心位置.
- 第五步, 比较新旧质心中距离的最大值. 如果最大值小于设定阈值ε(epsilon), 则判定算法收敛稳定; 否则继续迭代执行下一步骤.
- 第六步, 当满足收敛条件时算法终止; 否则返回第4步继续执行.
下图是对身高和体重进行聚类的算法,将数据集的人群聚集成三类。
二.K-Means聚类分割灰度图像
在图像处理领域中应用K-Means聚类算法可以实现多种操作如图象分割图象聚类和图象识别等基本功能。本节重点介绍基于该算法的颜色分割方法。考虑如下一个具有典型性的场景:一张尺寸为100×100像素的灰度图谱其色调范围包含着1万种可能的RGB组合我们可以通过K-Means算法将所有像素按照特征属性分为K个互不重叠的簇接着以各组质心色代表该组像素最终能够实现基于色域压缩的色彩层级划分效果
在OpenCV中,Kmeans()函数原型如下所示:
retval, bestLabels, centers由kmeans函数根据data、K、criteria、attempts以及 flags参数进行计算得到。
- data 用于表示聚类的数据,并且最好是 np.float32 类型的 N 维点集。
- K 用于表示聚类的类别数量。
- bestLabels 用于存储每个样本对应的聚类标签索引。
- criteria 用于确定算法终止的标准,具体包括最大迭代次数或所需的精度水平。在每次迭代中,当所有簇中心之间的移动距离均小于 criteria.epsilon 时,算法将停止执行。
- attempts 用于记录重复运行 K-means 算法以获得最佳结果的次数。
- flags 用于指定初始质心选择的方法:cv2.KMEANS_PP_CENTERS 或 cv2.KMEANS_RANDOM_CENTERS。
- centers 用于输出集群中心的位置信息矩阵。
本段采用该方法对灰度图像中的颜色分割进行处理,并需注意以下几点:在执行K-Means聚类之前,首先要将RGB像素值转化为一维数据序列;接着会整合这些色彩元素以完成最终的分割结果。
# coding: utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取原始图像灰度颜色
img = cv2.imread('scenery.png', 0)
print img.shape
#获取图像高度、宽度
rows, cols = img.shape[:]
#图像二维像素转换为一维
data = img.reshape((rows * cols, 1))
data = np.float32(data)
#定义中心 (type,max_iter,epsilon)
criteria = (cv2.TERM_CRITERIA_EPS +
cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
#设置标签
flags = cv2.KMEANS_RANDOM_CENTERS
#K-Means聚类 聚集成4类
compactness, labels, centers = cv2.kmeans(data, 4, None, criteria, 10, flags)
#生成最终图像
dst = labels.reshape((img.shape[0], img.shape[1]))
#用来正常显示中文标签
plt.rcParams['font.sans-serif']=['SimHei']
#显示图像
titles = [u'原始图像', u'聚类图像']
images = [img, dst]
for i in xrange(2):
plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray'),
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
如图所示的输出结果中,左侧展示的是原始灰度图像, 右侧则是经过K-Means聚类处理后的图像. 该算法将所有灰度等级划分为四个层次类别, 并将相近色调或连续区域整合到同一类别中.

三.K-Means聚类对比分割彩色图像
该算法实现了对彩色图像的颜色分割处理,并将其划分为不同类别。
# coding: utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取原始图像
img = cv2.imread('scenery.png')
print img.shape
#图像二维像素转换为一维
data = img.reshape((-1,3))
data = np.float32(data)
#定义中心 (type,max_iter,epsilon)
criteria = (cv2.TERM_CRITERIA_EPS +
cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
#设置标签
flags = cv2.KMEANS_RANDOM_CENTERS
#K-Means聚类 聚集成2类
compactness, labels2, centers2 = cv2.kmeans(data, 2, None, criteria, 10, flags)
#K-Means聚类 聚集成4类
compactness, labels4, centers4 = cv2.kmeans(data, 4, None, criteria, 10, flags)
#K-Means聚类 聚集成8类
compactness, labels8, centers8 = cv2.kmeans(data, 8, None, criteria, 10, flags)
#K-Means聚类 聚集成16类
compactness, labels16, centers16 = cv2.kmeans(data, 16, None, criteria, 10, flags)
#K-Means聚类 聚集成64类
compactness, labels64, centers64 = cv2.kmeans(data, 64, None, criteria, 10, flags)
#图像转换回uint8二维类型
centers2 = np.uint8(centers2)
res = centers2[labels2.flatten()]
dst2 = res.reshape((img.shape))
centers4 = np.uint8(centers4)
res = centers4[labels4.flatten()]
dst4 = res.reshape((img.shape))
centers8 = np.uint8(centers8)
res = centers8[labels8.flatten()]
dst8 = res.reshape((img.shape))
centers16 = np.uint8(centers16)
res = centers16[labels16.flatten()]
dst16 = res.reshape((img.shape))
centers64 = np.uint8(centers64)
res = centers64[labels64.flatten()]
dst64 = res.reshape((img.shape))
#图像转换为RGB显示
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
dst2 = cv2.cvtColor(dst2, cv2.COLOR_BGR2RGB)
dst4 = cv2.cvtColor(dst4, cv2.COLOR_BGR2RGB)
dst8 = cv2.cvtColor(dst8, cv2.COLOR_BGR2RGB)
dst16 = cv2.cvtColor(dst16, cv2.COLOR_BGR2RGB)
dst64 = cv2.cvtColor(dst64, cv2.COLOR_BGR2RGB)
#用来正常显示中文标签
plt.rcParams['font.sans-serif']=['SimHei']
#显示图像
titles = [u'原始图像', u'聚类图像 K=2', u'聚类图像 K=4',
u'聚类图像 K=8', u'聚类图像 K=16', u'聚类图像 K=64']
images = [img, dst2, dst4, dst8, dst16, dst64]
for i in xrange(6):
plt.subplot(2,3,i+1), plt.imshow(images[i], 'gray'),
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
输出结果如下图所示,当K=2颜色聚集成两种,当K=64颜色聚集成64种。

