【机器学习】Matlab中应用K-means算法实现图像压缩

1. K-means图片压缩算法步骤:
① 随机选择初始化的K个样本作为聚类中心,用数组centroids保存聚类中心的值,在本实验中,聚类的中心是图像的RGB值; ② 针对图片中的每个像素值,计算它到K个聚类中心的距离,并将其分配到距离最小的聚类中心所对应的类中。例如,第735个像素点距离centroids中目前的第2个聚类中心距离最近(此距离为欧式距离),则将idx[735]赋值为2; ③ 上一步完成后,idx(size==像素点个数)中储存了每个像素点对应的分类(一共有K个分类),针对每个分类idx[i],重新计算所有属于第k类的像素点的聚类中心,并更新centroids数组的值; ④ 回到②,直到达到某个中止条件,例如迭代次数、最小误差变化等。
2. 程序设计步骤:
① 数据预处理:在MATLAB中调用函数imread()读取图像后,获得一个shape=(x,y,3)的数组,储存的是二维图像的x*y个像素点所对应的R-G-B值,将二维的像素点reshape到一维中,因为只需要对像素点聚类,而不需要对坐标进行聚类。选择K的值,初始化聚类的质心,可以在x*y个像素点中随机选择K个作为聚类质心。

② 函数定义:本程序需要定义两个函数,第一个函数是为所有像素点寻找质心,方法是遍历计算每一个像素点到每一个质心的欧式距离,选择其中最近的距离作为自己的分类;第二个函数是对所有聚类重新分配质心,方法是遍历每一个分类,重新计算该分类的所有像素点的均值,并更新质心的像素值。

③ K-means训练:设置终止条件,本程序中设置迭代10次进行终止,在每一次迭代中依次执行上述K-means算法步骤中的②③步,迭代完成后获得每一个点的分类索引以及所有聚类中心的像素值。

④ K-means测试:通过上面第③步训练出了K个聚类中心,下面输入一张图片,先调用函数为这张图片的每一个像素值寻找一个最近的聚类中心,保存所有聚类中心的索引值以及该图每一个像素值对应聚类的索引,即可构建出原始图像,而不再需要保存每一个像素值的数据,实现了图片储存规模的压缩。

3. MATLAB代码:
代码结构:

在这里插入图片描述

# computeCentroids.m
function centroids = computeCentroids(X, idx, K)
    n = size(X, 2);
    centroids = zeros(K, n);
    % idx是第i个像素点的分类,接下来对所有分类的像素点求均值,即centroids
    tmp = [];
    for i = 1:K
        tmp = X(find(idx == i),:);
        centroids(i,:) = sum(tmp)/(size(tmp,1));
    end
end
# findClosestCentroids.m
function idx = findClosestCentroids(X, centroids, K)
    m = size(X, 1);
    % 第i个像素点最近的像素点
    idx = zeros(m, 1);
    tmp = zeros(K, 1);
    op = [];
    for i = 1:m
        for j = 1:K
            tmp(j) = (X(i, :) - centroids(j, :)) * (X(i, :) - centroids(j, :))';
        end
        op = find(tmp == min(tmp));
        idx(i) = op(1);
    end
end
# K-means.m
A = double(imread('bird_small.tiff'));
A = A / 255;
sz = size(A);

% 把二维的像素点映射到一维中
X = reshape(A, sz(1) * sz(2), 3);
m = size(X, 1);
% 初始化质心,首先获取随机索引值
randidx = randperm(m);
% 随机选取像素点的前K个像素点作为聚类中心
K = 256;
centroids = X(randidx(1:K), :);

% K-means 训练
idx = zeros(m, 1); 
max_step = 256;
for step = 1:max_step
    idx = findClosestCentroids(X, centroids, K);
    centroids = computeCentroids(X, idx, K);
end
% 训练完毕,centroids是K个质心的像素值,idx是每个像素点所属的质心

% 输入新的图片进行测试
B = double(imread('bird_large.tiff'));
B = B / 255;
sz = size(B);
Y = reshape(B, sz(1) * sz(2), 3);
% 把新图片的每一个像素值都按训练好的模型进行分类
idy = findClosestCentroids(Y, centroids, K);
% 一共有idy个像素,Yc表示对每个像素值的重新赋值
Yc = centroids(idy, :);
large_image = reshape(Yc, sz(1), sz(2), 3) .* 255;

%Show
imshow(uint8(round(large_image)))
%Save
imwrite(uint8(round(large_image)), 'bird_kmeans.tiff');
4. 结果分析:
1. 使用小图进行训练,使用大图进行测试,原小图:

.在这里插入图片描述
原大图:

在这里插入图片描述

其中小图size为128×128个像素值,大图size为538×538个像素值,K-means算法中选择聚类数目K=32,迭代次数max_step=20,压缩后的图片为:
在这里插入图片描述
可以发现,压缩后的图片此时与原图基本上一致,只在局部区域像素点饱和度较低。下面是一些不同的K值选取和不同迭代次数下图片的还原情况:

在这里插入图片描述

通过观察结果可以发现,当K=4,max_step=4的情况下,原图的轮廓已经能够还原出来;K=16,max_step=16的情况下,已经能够还原出原图的色域分布以及线条层次感;K=32,max_step=32的情况下,能够还原出原图的更多细节,以及一些比较小范围的颜色;继续增大K和max_step,图像颜色的饱和度和区分度更加明显,但提升的效果有限,且花费的时间大大增加。

5. K-means的优缺点:
①优点:算法复杂度低,对于小样本来说性能较好,往往可以达到局部最优; ②缺点:K值需要认为设定,算法对初试的聚类中心较为敏感,当K值增加时算法的时间也会大大增加。
6. 算法的调优与改进:
①数据预处理:K-means 的本质是基于欧式距离的数据划分算法,均值和方差大的维度将对数据的聚类产生决定性影响。所以未做归一化处理和统一单位的数据是无法直接参与运算和比较的。常见的数据预处理方式有:数据归一化,数据标准化。此外,离群点或者噪声数据会对均值产生较大的影响,导致中心偏移,因此我们还需要对数据进行异常点检测。

②合理选择K值:K 值的选取对 K-means 影响很大,这也是 K-means 最大的缺点,常见的选取 K 值的方法有:手肘法、Gap statistic 方法。手肘法:

在这里插入图片描述

当 K < 3 时,曲线急速下降;当 K > 3 时,曲线趋于平稳,通过手肘法我们认为拐点 3 为 K 的最佳值。

③采用核函数:基于欧式距离的 K-means 假设了了各个数据簇的数据具有一样的的先验概率并呈现球形分布,但这种分布在实际生活中并不常见。面对非凸的数据分布形状时我们可以引入核函数来优化,这时算法又称为核 K-means 算法,是核聚类方法的一种。核聚类方法的主要思想是通过一个非线性映射,将输入空间中的数据点映射到高位的特征空间中,并在新的特征空间中进行聚类。非线性映射增加了数据点线性可分的概率,从而在经典的聚类算法失效的情况下,通过引入核函数可以达到更为准确的聚类结果。

  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值