Matlab K-means聚类算法对多光谱遥感图像进行分类(一)

Matlab K-means聚类算法对多光谱遥感图像进行分类

作者: 白艺亭

测试了下matlab自带kmeans函数,作者编写函数,以及ENVI下的Kmeans方法,对比其效果,代码及结果图展示见下。(K均值聚类的matlab代码,对其算法进行实现。)

1. K-Means无监督聚类算法:

ENVI中,在主菜单上,选择Classification>>Unsupervised>>K-Means,在Classification Input File中选择TM影像,单击OK。打开K-means Parameters对话框中,设置以下的参数:
1)分类数量(Number of Classes):一般为最终输出分类数量的2-3倍。
2)变换阈值(Change Threshold):5。每当一类的变化像元数小于阈值时,结束迭代过程。这个值越小得到的结果越精确,运算量也越大。
3)最大迭代次数(Maximum Iterations):30(我选的).迭代次数越大,精度越高。
4)距离类别的值的最大误差(Maximum Stdev From Mean)。此数可选。
5)允许的最大距离误差:(Maximum Distance Error);可选
6)选择路径,OK执行。
在这里插入图片描述
以上参考东腾的博客

2. 自编matlab函数实现

2.1被调function

function [new_class_label] = Kmeans_of_muldim(data,k,change_threshold,iteration)
% 功能:实现多光谱遥感数据非监督分类算法之K-means聚类算法
%Author: Mr. BAI
% 输入:data是s*fl*b的矩阵,s为列数(sample),fl为行数(fileline),b为波段数(band);
%       k 为类别数,如果有背景值,背景值会归到某一地类中去,到时再用矢量边界图形裁剪一下即可。我考虑过将出现次数最多
%       的背景值单独划归一类,但是程序设计时不好判断,取数组中元素出现次数最多的像元为一类,有点大胆,因为无背
%       景的图像像元值也可能出现这种情况;
%       change_threshold变化阈值,ENVI中默认为0.05%       iteration为最大迭代次数,ENVI中默认为1
% 输出:new_class_label为聚类后的矩阵,赋予每个行列号一个类别标签,之后可在GIS或者ENVI中出图
% Reference:https://www.cnblogs.com/dongteng/p/5415071.html
    [fl,s,b] = size(data);
    %original_seed为迭代前的种子,存放一个k行,b个波段数值列的数组
    old_seed = zeros(k,b);
    %newseed为迭代后的新种子,存放一个k行,b个波段数值列的数组
    new_seed = zeros(k,b);
    %-------------------------------------------------------------------------------------------------
    % 产生k个随机种子作为遥感图像各地物类别的种子像元
    %-------------------------------------------------------------------------------------------------
    index_record = zeros(1,k);
    for i = 1:k 
        index_i = round(rand()*fl*s);
        judge = find(index_record == index_i);
        %如果已经有这个值了,那么重新循环取值
        if isempty(judge) == 0
            i = i-1;
            continue;
        end
        index_record(i) = index_i;
        %计算取到的随机值对应图像的行列号
        fl_index = floor(index_i/s);%行号
        sample_index = index_i - fl_index*s;%列号
        %将该种子像元的b个波段值存入
        old_seed(i,:) = data(fl_index,sample_index,:);
    end
    %--------------------------------------------------------------------------------------
    % 下面进行迭代,如果本次分别所有类新得到的像元数目变化在change_threshold内,则认为分类完毕。
    %--------------------------------------------------------------------------------------
    n = 1;
    new_class_label = zeros(fl,s);
    while n
        distance_matrix = zeros(fl,s,k);
        for kind = 1:k
            sum = 0;
            for i=1:b
               temp = power(abs(data(:,:,i)-old_seed(kind,i)),2);
               sum = sum+temp;
            end
            %每个像元与初始7个类别中心的欧式距离
            ou_distance = sqrt(sum);
            distance_matrix(:,:,kind) = ou_distance;
        end
        %给给各类别赋值类别标注
        for i=1:fl
            for j=1:s
                currentpixel_vector = distance_matrix(i,j,:);
                currentpixel_class = find(currentpixel_vector == min(currentpixel_vector));
                new_class_label(i,j) = currentpixel_class(1);
            end
        end
        %计算新的各类别中心
        for i=1:k
            id = find(new_class_label==i);
            for j=1:b
                temp1 = data(:,:,j);
                temp2 = temp1(id);
                new_seed(i,j)= mean(temp2(:)); 
            end
        end    
        new_class_pixcel_number = zeros(1,k);
        for i=1:k
            new_class_pixcel_number(i) = length(find(new_class_label(:)==i));
        end
        %Change threshold:0.05
        if n == 1
            old_class_pixcel_number = ones(1,k);
        end
        %size(new_class_pixcel_number)
        if max(abs((new_class_pixcel_number-old_class_pixcel_number)./old_class_pixcel_number)) < change_threshold || n>iteration
            break;
        end
        n=n+1;
        if max(abs((new_class_pixcel_number-old_class_pixcel_number)./old_class_pixcel_number)) >change_threshold
            %old_class_label = new_class_label;
            old_class_pixcel_number = new_class_pixcel_number;
            old_seed = new_seed;
            continue;
        end 
    end
end

2.2 main函数调用上述function

clc;
clear;
t0 = cputime;
cd 'E:\MATLAB\'
data=imread('nantong_city_landsat8.tif');%读取纯数据
[multi_data,r]=geotiffread('nantong_city_landsat8.tif');  % read the geo information
info=geotiffinfo('nantong_city_landsat8.tif');   % read the geo information
class_result = Kmeans_of_muldim(data,5,0.05,30);
geotiffwrite('K-means_class.tif',class_result,r,'GeoKeyDirectoryTag',info.GeoTIFFTags.GeoKeyDirectoryTag);
t1 = cputime;
during = t1 - t0;
disp('耗时:');
disp(during);
耗时:
                 139.59375

时间长是因为程序中Kmeans_of_muldim()函数中使用了逐像元循环,用了139秒,后面附上改进的算法, MKmeans_of_muldim(),Matlab K-means聚类算法改进对多光谱遥感图像进行分类(二)。

3 matlab库中的kmeans()

clc;
clear;
%matlab自带的kmeans使用方法:
t0 = cputime;
cd 'E:\MATLAB\'
data=imread('nantong_city_landsat8.tif');%读取纯数据
[multi_data,r]=geotiffread('nantong_city_landsat8.tif');  % read the geo information
info=geotiffinfo('nantong_city_landsat8.tif');   % read the geo information
[fl,s,b] = size(data);
dat = zeros(fl*s,b);%matlab K-means算法要求输入矩阵是一个列向量组成的矩阵,列数为波段数,每一列为fl*s的像元值
for i=1:b
    dat(:,i) = reshape(data(:,:,i),fl*s,1);
end
class_result = kmeans(dat,5);只能传两参
out_data = reshape(class_result,fl,s);
geotiffwrite('K-means_class_matlab.tif',out_data,r,'GeoKeyDirectoryTag',info.GeoTIFFTags.GeoKeyDirectoryTag);
t1 = cputime;
during = t1 - t0;
disp('耗时:');
disp(during);
耗时:
                 38.796875

4 结果展示

4.1ENVI操作结果展示:

在这里插入图片描述
0.05,30.

4.2 Kmeans_of_muldim,作者的matlab代码结果展示:

在这里插入图片描述
0.05,30

4.3K-means matlab自带函数结果展示:

在这里插入图片描述
效果相当,下面对Kmeans_of_muldim进行改进。
见下一篇:
Matlab K-means聚类算法改进对多光谱遥感图像进行分类(二)


后来,我们都学会了勇敢,不遗憾,不留恋。417看完了刘若英的线上演唱会,1500w人同时在线的,很壮观。

猜你喜欢

转载自blog.csdn.net/qq_37970770/article/details/105606207