Machine Learning-Clustering-语义分割

本文使用机器学习中的聚类方法来对图像进行分割,下面将会介绍两种方法实现:K-means、Fuzzy C-means

首先,输入为一张图片,图片由RGB三通道构成,我们可以通过对图像的每个像素点进行展开,再进一步对每个像素点进行聚类。

以该图像为例:

 我们导入PIL库,引入Image包

img = Image.open('./2.png')
plt.imshow(img)
plt.show()

我们将图片转换成数组形式:

img = np.asarray(img)
m,n,d = img.shape
m,n,d

可以看见数组的维度信息为:     (218, 252, 3)

接着,我们将数组展开:

data = img.reshape(m*n,d)
data.shape

将图片上的每个像素点都作为一行,得到了新的数组,其维度为:(218*252,3),即(54936, 3)

接下来,我们就可以使用K-means或者Fuzzy C-means来对其进行聚类:

其原理为:当前的二维数组的每一行有三个值,可以将其视为第 i 个像素的特征值,对应于一个特征向量,所以每个像素点都有一个特征向量,那么,这个特征向量就是由RGB三原色构成。从肉眼的角度看待,我们可以自然的认为颜色相接近的是同一类,这样,就可以使得聚类顺利的进行。

K-means代码:
def init_centroids(X, k):
    m, n = X.shape
    centroids = np.zeros((k, n))
    idx = np.random.randint(0, m, k)

    for i in range(k):
        centroids[i, :] = X[idx[i], :]

    return centroids

## 给定均值或者中心后,按照距离最小原则把样本归类。
## idx 存储了每个样本属于的类别
def find_closest_centroids(X, centroids):
    m = X.shape[0]
    k = centroids.shape[0]
    idx = np.zeros(m)

    for i in range(m):
        min_dist = np.inf
        for j in range(k):
            dist = np.sum((X[i, :] - centroids[j, :])**2)
            if dist < min_dist:
                min_dist = dist
                idx[i] = j

    return idx
## 对样本进行划分后,要更新类别的均值

def compute_centroids(X, idx, k):
    m, n = X.shape
    centroids = np.zeros((k, n))

    for i in range(k):
        indices = np.where(idx == i) #indices为二维数组,所以下面axis=1,按行进行取平均 或者 先squeeze让indices变为一维,再axis=0取列平均
        centroids[i, :] = (np.sum(X[indices, :], axis=1) /
                           len(indices[0])).ravel()

    return centroids
## 执行k-means 算法的迭代过程

def run_k_means(X, initial_centroids, max_iters):
    m, n = X.shape
    k = initial_centroids.shape[0]
    idx = np.zeros(m)
    centroids = initial_centroids
    
    for i in range(max_iters):
        idx = find_closest_centroids(X, centroids)
        centroids = compute_centroids(X, idx, k)
    
    return idx, centroids
Fuzzy C-means代码:
def FCM(X, c, m, eps, max_its): 
#x代表数据,c代表聚类数目,m是加权指标,eps是差别,max_its是最大迭代次数  
    #矩阵u初始化  
    num = X.shape[0]
    u = np.random.random((num, c))
    u = u/np.sum(u, axis=1)[:, np.newaxis]          #[:, np.newaxis]将行向量转换为列向量     归一化u矩阵,使得每行加和等于1
    it = 0  
    while it<max_its:  
        it+=1  
        um = u ** m    
        center = np.dot(um.T, X)/(np.sum(um.T, axis=1)[:, np.newaxis])  
        
        distance = np.zeros((num, c))  
        for i, x in enumerate(X):  
            for j, v in enumerate(center):  
                distance[i][j] = np.sum((x - v)**2)
        
        new_u = np.zeros((len(X), c))   
        for i in range(num):  
            for j in range(c):
                new_u[i][j] = 1. / np.sum((distance[i][j] / distance[i]) ** (2 / (m - 1)))  
        if np.sum(abs(new_u - u)) < eps:  
            break  
        u =  new_u
    return np.argmax(u, axis=1) #返回每一行隶属度最大的类

在运行前,我们需要先肉眼判断图片中大概有几个类,指定迭代的次数和其余的超参数,运行代码,得到每个像素的预测类别

使用K-means聚类

initial_centroids = init_centroids(data,classes)
idx, centroids = run_k_means(data, initial_centroids, 10)

使用FCM聚类

idx= FCM(data, 5, 2, 1e-15, 10)

那么,idx即为每个像素的预测类别,得到了这个之后,我们需要其以图像的形式展现出来。

我们可以轻松的想到,将不同类别染为不同的颜色,为此,我们定义出一个colors数组

color0 = np.array([20,20,20])
color1 = np.array([80,80,80])
color2 = np.array([150,150,150])
color3 = np.array([250,250,250])
color4 = np.array([200,200,200])
colors = np.array([color3,color1,color4,color2,color0])

根据idx的内容,将原data数组中对应位置的特征向量,替换为我们指定的颜色(特征向量)

idx_unique = np.unique(idx)

index = dict()
for i in idx_unique:
    index[i] = np.where(idx==i)[0]

cluster1 = data[np.where(idx == 0)[0],:]#从X中取出隶属于当前类别的行,np.where(idx==0).shape =》(1,c)  后面加[0],则变为一个列表,长度为c
cluster2 = data[np.where(idx == 1)[0],:]
cluster3 = data[np.where(idx == 2)[0],:]
cluster4 = data[np.where(idx == 3)[0],:]

color0 = np.array([20,20,20])
color1 = np.array([80,80,80])
color2 = np.array([150,150,150])
color3 = np.array([250,250,250])
color4 = np.array([200,200,200])
colors = np.array([color3,color1,color4,color2,color0])

x = np.asarray(data,dtype=int)  #将原数组复制出来,再进行下面相应的替换;使用原数组无法替换

#染色
for i in range(len(idx_unique)):
    x[index[i]] = colors[i]

到这里,我们再次查看一下x数组的维度信息:

x.shape
(54936, 3)

维度与刚展开的时候相同,那么,我们现在需要将这个数组重新reshape为原始的三维数组:

result = np.asarray(x.reshape(m,n,d))
result.shape
(218, 252, 3)

#取特征向量的平均,转换为灰度图像,维度被压缩
result = np.average(result,axis=2)
result.shape
(218, 252)

这个时候,我们就可以使用Image.fromarray函数,将这种灰度图像展示出来:

res = Image.fromarray(result)
plt.imshow(res)
plt.show()

结果如图:

 K-means运行结果:

Fuzzy C-means运行结果:

 

从运行结果可以看出,K-means和Fuzzy C-means可以很好的完成分割任务

两种方法的对比:

K-means:

k-means算法只有在簇的平均值被定义的情况下才能使用。

k-means算法的不足之处在于它要多次扫描数据库。

k-means算法只能找出球形的类,而不能发现任意形状的类。

初始质心的选择对聚类结果有较大的影响。

k-means算法对于噪声和孤立点数据是敏感的,少量的该类数据 能够对平均值产生极大的影响。

Fuzzy C-means:

fuzzy c-means在k-means的基础上,改善了k-means算法对于噪声和孤立点敏感。

问题与思考:

根据颜色进行分类,k-means和fuzzy c-means都是使用欧式距离进行类别划分,为什么图片中可以不规则形状的进行分类(帽子上的空心椭圆)?

答:在文章的开始,我们就先将图片中的每一个像素进行展开,人眼看见的它的现状虽然是不规则的,但是从像素角度来说,它打破了原始的形状的这个限制,从而实现跨区域的进行分类。其分类的依据是每个像素的特征向量,这个特征就是图片的RGB值,其颜色越相似,其特征就越接近。

运用K-means算法进行图像分割, K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。 k个初始类聚类中心点的选取对聚类结果具有较大的 公式 公式 影响,因为在该算法第一步中是随机的选取任意k个对象作为初始聚类的中心,初始地代表一个簇。该算法在每次迭代中对数据集中剩余的每个对象,根据其与各个簇中心的距离将每个对象重新赋给最近的簇。当考察完所有数据对象后,一次迭代运算完成,新的聚类中心被计算出来。如果在一次迭代前后,J的值没有发生变化,说明算法已经收敛。 算法过程如下: 1)从N个文档随机选取K个文档作为质心 2)对剩余的每个文档测量其到每个质心的距离,并把它归到最近的质心的类 3)重新计算已经得到的各个类的质心 4)迭代2~3步直至新的质心与原质心相等或小于指定阈值,算法结束 具体如下: 输入:k, data[n]; (1) 选择k个初始中心点,例如c[0]=data[0],…c[k-1]=data[k-1]; (2) 对于data[0]….data[n],分别与c[0]…c[k-1]比较,假定与c[i]差值最少,就标记为i; (3) 对于所有标记为i点,重新计算c[i]={ 所有标记为i的data[j]之和}/标记为i的个数; (4) 重复(2)(3),直到所有c[i]值的变化小于给定阈值。 折叠工作原理 K-MEANS算法的工作原理及流程 K-MEANS算法 输入:聚类个数k,以及包含 n个数据对象的数据库。 输出:满足方差最小标准的k个聚类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值