图像分割之k-means聚类分割

k-means聚类原理

k-means聚类是一种无监督学习的聚类算法,它的目的是将数据集中的样本划分成若干个类别,使得同一类别内的样本相似度高,而不同类别之间的样本相似度低。其目标是将数据点划分为k个类簇,找到每个簇的中心并使其度量最小化。
此外,K-means算法有一些优点,如简单易懂、计算速度快,适合处理大数据集。但它也有一些缺点,比如需要预先指定簇的数量K,对初始聚类中心的选择敏感,可能收敛到局部最优而非全局最优解,对于非凸数据集和噪声数据敏感。
在实际应用中,K-means算法广泛应用于市场细分、图像分割、社交网络分析等领域。本文主要将其应用于图像分割领域。

k-means基本思路

通过计算相似度(默认欧氏距离),将相似度大的样本聚集到同一个类别,k表示聚成k个类别,means表示每个类别的聚类中心点是通过簇中所有样本点的均值得到。
在这里插入图片描述

聚类效果评估

聚类的效果可采用误差平方和SSE进行评价,SSE值越小,表示数据点越接近它们的中心点,聚类效果越好。
在这里插入图片描述

k-means聚类算法流程

下面是K-Means聚类算法的分析流程,步骤如下:

第一步,确定K值,即将数据集聚集成K个类簇或小组。
第二步,从数据集中随机选择K个数据点作为质心(Centroid)或数据中心。
第三步,分别计算每个点到每个质心之间的距离,并将每个点划分到离最近质心的小组,跟定了那个质心。
第四步,当每个质心都聚集了一些点后,重新定义算法选出新的质心。
第五步,比较新的质心和老的质心,如果新质心和老质心之间的距离小于某一个阈值,则表示重新计算的质心位置变化不大,收敛稳定,则认为聚类已经达到了期望的结果,算法终止。
第六步,如果新的质心和老的质心变化很大,即距离大于阈值,则继续迭代执行第三步到第五步,直到算法终止。

对于图像分割任务,需要在第一步之前将图像reshape为m*n行、p列的矩阵,这样每一行就是一个p维数据点。通过以上聚类算法,得到最终的聚类结果,最后再将结果reshape为原始图像的大小即可。

MATLAB实现

MATLAB自带函数

kmeans函数的说明如下:

idx = kmeans(X,k) 执行 k 均值聚类,以将 n×p 数据矩阵 X 的观测值划分为 k个聚类,并返回包含每个观测值的簇索引的 n×1 向量 (idx)。X 的行对应于点,列对应于变量。

根据上面的kmeans函数的说明,我们需要将图像矩阵reshape一下,然后再调用该函数,最后再reshape回来,下面是测试的主要代码:

[m,n,p]=size(img)              %m,n为所求,p=3为通道数
% 将图像进行RGB——3通道分解
% org(:, :, 1)......分别代表rgb通道
A = reshape(img(:, :, 1), m*n, 1);   
B = reshape(img(:, :, 2), m*n, 1);
C = reshape(img(:, :, 3), m*n, 1);
data = [A B C];  % r g b分量组成样本的特征,每个样本有三个属性值,共width*height个样本

% kmeans第一个参数: N*P的数据矩阵,N为数据个数,P为单个数据维度
res = kmeans(double(data), k);        
result = reshape(res, m, n);  %转化为图片形式

MATLAB实现

根据上述算法流程,使用matlab编写如下代码,其中设置强制截断的迭代次数,并添加了迭代过程展示。
my_kmeans函数:

function [C, label, J] = my_kmeans(data, k, is_gpu, is_show) 
%% 语法 :
% [C, label, J] = my_kmeans(data, k, is_gpu, is_show) 
%% 说明:
% data  为输入图像
% k  为聚类的k个数据中心
% is_gpu  是否启用gpu加速
% is_show  参数决定是否显示聚类过程
% ----------------------------------------
% return label  为返回的聚类结果
% return C  聚类中心
% return J  每次迭代的误差
%%
if nargin >= 3
    is_gpu = 1;
else
    is_gpu = 0;
end
if nargin == 4
    is_show = 1;
else
    is_show = 0;
end
%%
% data可以为一维数据
m = size(data, 1);
n = size(data, 2);
p = size(data, 3);
% [m, n, p] = size(I); %图片的大小m*n,p代表RGB三层
X = reshape(double(data), m*n, p);
if is_gpu
    X = gpuArray(X);
end
rng('default');
C = X(randperm(m*n, k), :);%随机选k个聚类中心

J = [];
J_prev = inf; 
tol = 1e-1; %容忍度tol,inf为无穷大
iter = 0; 
rand_c = rand(k,3)*255;
rand_c = uint8(rand_c);
while true
    iter = iter + 1;
    dist = sum(X.^2, 2)*ones(1, k) + (sum(C.^2, 2)*ones(1, m*n))' - 2*X*C'; %计算各个点到K个聚类中心的距离
    [~,label] = min(dist, [], 2) ;  %label记录最小值的行数
    for i = 1:k
       C(i, :) = mean(X(label == i , :)); %取新的k个聚类中心
    end
    J_cur = sum(sum((X - C(label, :)).^2, 2)); %距离之和
    J = [J, J_cur];
    fprintf('#iteration: %03d, objective norm diff: %f\n', iter, norm(J_cur-J_prev, 'fro'));
    if norm(J_cur-J_prev, 'fro') < tol   % A和A‘的积的对角线和的平方根,即sqrt(sum(diag(A'*A))),本次与上次距离之差
        break;
    end
    if (iter==100)  %设置强制截断的迭代次数
        break;
    end
    J_prev = J_cur;
    if is_show
        % 录制gif
        rand_c = uint8(C);
        temp = uint8(label);
        temp = rand_c(temp,:);
        sg=reshape(temp,m,n, []);
        imshow(mat2gray(sg));
        F=getframe(gcf);
        data=frame2im(F);
        [data,map]=rgb2ind(data,256);
        if iter == 1
            imwrite(data,map,'test.gif','gif','Loopcount',inf,'DelayTime',0.2);
        else
            imwrite(data,map,'test.gif','gif','WriteMode','append','DelayTime',0.2);
        end
    end
end
% C = rand_c;
end

测试代码如下,如果是单纯是为了处理图像,可以将“聚类”这小段的代码重新封装成一个function。

clear;
close all;
clc;
tic
%% 读取数据
img=imread('city.png');
% img = rgb2gray(img);
%% 聚类
m = size(img, 1);
n = size(img, 2);
p = size(img, 3);
k = 4;
[C, label, J2] = my_kmeans(img, k, 1, 1);
% 使用gather函数将gpuarray转换为普通数组
C = gather(C);
C = uint8(C);
% randn('seed', sum(100*clock))
% sprintf('seed=%d', sum(100*clock))
% rand_c = rand(k,3)*255;
% C = uint8(rand_c);
label = uint8(label);
temp = C(label,:);
dst = reshape(temp, m, n, []);%转换成图片矩阵的格式
toc
%% 显示结果
figure
subplot(221), imshow(img,[]);
subplot(222), imshow(dst,[]);
subplot(212), plot(J2), xlabel('iters'), ylabel('norm diff')

测试结果

测试的原始图片如下:
在这里插入图片描述
聚类过程如下,是不是有种延时摄影的感觉。
在这里插入图片描述
测试结果及相应的迭代过程如下所示:
在这里插入图片描述

MATLAB自带函数不同K值下的效果:
在这里插入图片描述
自己实现的不同K值下的效果:
在这里插入图片描述
MATLAB自带函数的效果和我实现的效果有所区别,主要是由于MATLAB自带函数采用了kmeans++进行了优化,在此就不赘述了,感兴趣的朋友可以去深入了解。

参考文献

[1] 机器学习实战——K-means聚类图像分割
[2] 基于K均值聚类算法的图像分割(Matlab)

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
运用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、付费专栏及课程。

余额充值