个人学习笔记:采用聚类方法对图像进行分割,以下内容纯粹个人理解,如有错误请帮我指出!多谢!
图像分割就是把图像按照某些条件分成不同的区域,并提取出感兴趣的区域。传统的分割方法包括基于阈值的分割、基于区域的分割、基于边缘的分割等。当然,本次笔记写的是采用K均值聚类实现图像分割。(代码在文章结尾)
一、理论分析
K均值是一种比较常用的聚类算法,由于并不是本次笔记的重点,因此只进行算法的流程简单记录如下:
1、选取K个样本作为聚类中心
2、计算各个样本到聚类中心的距离
3、更新均值向量,重复以上步骤
分割算法核心:对图像的像素进行聚类,相似RGB值的像素被聚到一起,就形成了K个区域!
问题的核心在于如何使用K均值对图像的像素进行聚类,自己整理了以下的几个问题:
1、 K均值使用什么做样本?
K均值聚类就是把图像的像素点按照“值的接近程度”进行聚类,那么就会给每个像素点打标签(把每个位置R、G、B值看作是一个样本,R、G、B就是三个特征),有多少像素值(R、G、B算一个像素值)就有多少个样本。
2、 K均值得到的标签含义是什么?
K均值得到的结果标签就是width*height个0,1,2这种值,因为图像提供的样本数就是width*height个,也就是对每个位置的像素点打标签。含义就是亮度接近的像素标签值相同。
3、K均值聚类以后如何得到新的聚类图像
像素点的标签值表示它属于哪个类,后续使用标签值作为亮度值。如果是聚类结果为3, 那么标签值就有0,1,2三个,标签的shape是width*height,那么就是用0,1,2填充一张np.zeros(width, height)图像,就得到了最后的聚类结果。
标签值相同的位置因此也就亮度相同,得到的聚类图像仅仅包含k个亮度种类,比如K=3,那么聚类图像就只有三种亮度值
实现代码里面未解决的Bug:因为标签值0,1,2代表的仅仅是三个不同区域,但是没办反设定哪个区域是2(最亮的区域),也就是最后合成的图像哪个区域亮没办反控制(比如人脸区域应该更亮),只能多运行几次程序。
二:代码实现
算法流程分为以下四步:
1、读取图片
2、将图片像素转为样本形式
3、对样本进行聚类
4、创建空白图像
5、使用聚类结果对空白图像进行填充
6、保存聚类得到的标签图
步骤1和2:
# 1:read image
上述代码块的#1负责读取图片,#2负责把(w,h,3)的图像转为(w*h,3)的数据,每行就是一个样本,每个样本包含R、G、B三个特征值。
步骤3:
# 3: cluster, I thought: give every pixel (that in orignal image)
# a label , so the label have same shape as image(gray)
cls = KMeans(n_clusters=k_cluster).fit_predict(data)
cls = cls.reshape(image.shape[0], image.shape[1])
聚类就是对上述的像素值进行聚类
步骤4:
# 4: create a image container
container = np.zeros(shape=(image.shape[0], image.shape[1]))
创建的像素值全为0的空白图像,用于存储聚类标签。
步骤5:
# 5: use cluster labels as "gray value"
# and fill it into aimage container
for i in range(image.shape[0]):
for j in range(image.shape[1]):
# cls[i, j]*30 ,because label value is 0, 1, 2
# the bright difference betwwen labels is to small
container[i, j] = cls[i, j]*60
将聚类标签值cls[i, j]乘以60作为亮度值(乘以70或者任何值都行,只要保证K*任何值不超过255)
步骤6:
# 6: saver the cluster image
container = container.astype(np.uint8)
imageio.imsave(save_name, container)
保存的时候一定要转换为uint8编码
完整的代码如下:
import numpy as np
import imageio
from sklearn.cluster import KMeans
def image_cluster(image_name, save_name, k_cluster=3):
"""
cluster by KMeans for RGB image
"""
# 1:read image
image = imageio.imread(image_name)
# 2: convert (w, h, 3) into (w*h, 3)
# R,G and B combine as 3 features
# data will be a 2D matrix, each row have 3 values(R/G/B),
# and each column has width*height values
# this operation convert 3D to 2D, like reshape
image2matrix = []
for i in range(image.shape[0]):
for j in range(image.shape[1]):
r_v, g_v, b_v = image[i, j]
image2matrix.append([r_v/255.0, g_v/255.0, b_v/255.0])
data = np.mat(image2matrix)
# 3: cluster, I thought: give every pixel (that in orignal image)
# a label , so the label have same shape as image(gray)
cls = KMeans(n_clusters=k_cluster).fit_predict(data)
cls = cls.reshape(image.shape[0], image.shape[1])
# 4: create a image container
container = np.zeros(shape=(image.shape[0], image.shape[1]))
# 5: use cluster labels as "gray value"
# and fill it into aimage container
for i in range(image.shape[0]):
for j in range(image.shape[1]):
# cls[i, j]*30 ,because label value is 0, 1, 2
# the bright difference betwwen labels is to small
container[i, j] = cls[i, j]*60
# 6: saver the cluster image
container = container.astype(np.uint8)
imageio.imsave(save_name, container)
return True
image_cluster("data/vivian.jpg", "results/cluster.jpg")
对标题上费雯丽的照片进行聚类结果如下:
![f0687b148bb2169d6278460c4fdf72be.png](https://i-blog.csdnimg.cn/blog_migrate/8fb7b10ec84de6b687fe0c5905c92ab0.jpeg)
个人认为:K如果选择为2,那么图片视觉上看就应该包含“2种”明显不同的区域;K如果选择为3,那么图片中就应该包含3种明显不同的区域。