Matlab:基于颜色的分割

3
今天早些时候,我正在编写一个基于http://www.mathworks.com/products/demos/image/color_seg_k/ipexhistology.html和Matlab答案的脚本。
clc;
clear;
close all;
input_im=imread('C:\Users\Udell\Desktop\T2.jpg');
sz_im=size(input_im);

cform = makecform('srgb2lab');
lab_he = applycform(input_im,cform);
ab = double(lab_he(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);

nColors = 3;
% repeat the clustering 3 times to avoid local minima
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', 'Replicates',3);

pixel_labels = reshape(cluster_idx,nrows,ncols);
%imshow(pixel_labels,[]), title('image labeled by cluster index');
segmented_images = cell(1,3);
rgb_label = repmat(pixel_labels,[1 1 3]);

for k = 1:nColors
    color = input_im;
    color(rgb_label ~= k) = 0;
    segmented_images{k} = color;
end

for k=1:nColors
%figure
title_string=sprintf('objects in cluster %d',k);
%imshow(segmented_images{k}), title(title_string);
end


finalSegmentedImage=segmented_images{1};
%imshow(finalSegmentedImage);
close all;

Icombine = [input_im finalSegmentedImage];
imshow(Icombine);

在运行脚本多次时,我发现当finalSegmentedImage=segmented_images{1}用于组合图像(Icombine)时,得到的图像是不同的。为什么?如何解决这个问题,使结果重复(例如,segmented_images {1}图像将始终相同)?
非常感谢。
这是图片:

enter image description here

1个回答

7
您获得不同结果的原因是因为您的颜色分割算法使用了k-means聚类。我假设您不知道这是什么,因为熟悉其工作方式的人会立即告诉您,这就是为什么每次运行代码后您会获得不同结果的原因。事实上,您每次运行此代码后获得的不同结果是k-means聚类的自然结果,我将解释其中的原因。
它的工作原理是针对某些数据,您想将它们分成k组。您最初选择数据中的k个随机点,并且这些点将具有来自1,2,...,k的标签。这就是我们所谓的质心。然后,您确定其余数据与每个这些点的接近程度。然后,您将这些点分组,以便最接近任何这些k点的点都被分配为属于该特定组(1,2,...,k)的点。之后,对于每个组的所有点,您会更新质心,其实是每个组的代表点。对于每个组,您计算每个k组中所有点的平均值。这些成为下一次迭代的质心。在下一次迭代中,您确定数据中每个点与每个质心的接近程度。您不断迭代并重复此行为,直到质心不再移动或它们几乎不再移动。
这与上述代码的应用是,你正在使用只有k种可能颜色来表示图像。每个可能的颜色因此都是一个质心。一旦找到每个像素属于哪个簇,就可以用该像素所属簇的质心替换像素的颜色。因此,对于图像中的每个彩色像素,你都想决定它最适合用k种可能的颜色之一来表示。这是一个颜色分割的原因,因为你正在将图像分割为仅属于k种可能颜色。在更一般的意义上,这被称为无监督分割。
现在回到k均值问题。选择初始质心的方式是导致你获得不同结果的原因。你正在以默认方式调用k均值算法,它会自动确定算法将从哪些初始点开始选择。因此,每次调用该算法时,你不能保证生成相同的初始点。如果你想要重复相同的分割结果,无论你调用kmeans多少次,你需要自己指定初始点。因此,你需要修改k均值调用,使它看起来像这样:
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
                                      'Replicates', 3, 'start', seeds);

请注意,调用方式相同,但我们添加了两个额外的参数到 k-means 调用中。标志 start 表示您正在指定初始点,而 seeds 是一个大小为 k x p 的数组,其中 k 是您想要的组数。在此情况下,这与 nColors 相同,即 3。 p 是您数据的维度。由于您转换和重塑数据的方式,因此这将是2。因此,您最终指定了一个 3 x 2 矩阵。但是,在那里有一个 Replicate 标志。这意味着 k-means 算法将运行您指定的次数,并输出具有最小误差的分割。因此,我们将根据此标志指定的次数重复执行 kmeans 调用。上述 seeds 结构将不再是 k x p,而是 k x p x n,其中 n 是您要运行分割的次数。现在这是一个三维矩阵,每个二维片确定算法每次运行的初始点。记住这一点以备后用。
你如何选择这些点是由你决定的。然而,如果你想随机选择这些点而不是让它自己决定,并且想要在每次调用此函数时重现相同的结果,你应该将随机种子生成器设置为已知数字,例如123。这样,当你生成随机点时,它总会生成相同的点序列,因此是可重复的。因此,在调用kmeans之前,我建议你将这个代码添加进去。
rng(123); %// Set seed for reproducibility
numReplicates = 3;
ind = randperm(size(ab,1), numReplicates*nColors); %// Randomly choose nColors colours from data
                                                   %// We are also repeating the experiment numReplicates times

%// Make a 3D matrix where each slice denotes the initial centres for each iteration
seeds = permute(reshape(ab(ind,:).', [2 nColors numReplicates]), [2 1 3]);

%// Now call kmeans
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
                                      'Replicates', numReplicates, 'start', seeds);

请记住,您指定了Replicates标志,我们想要重复这个算法一定次数。这是3。因此,我们需要为每次运行算法指定初始点。因为我们将有3个点簇,并且我们将运行此算法3次,所以我们总共需要9个初始点(或nColors * numReplicates)。每组初始点必须是3D数组中的一个片段,这就是为什么在kmeans调用之前会看到那个复杂的语句。
我将复制次数作为变量,这样您可以随心所欲地更改它,而且它仍然有效。带有permutereshape的复杂语句使我们能够非常轻松地创建这个点的3D矩阵。
请记住,最近在MATLAB中调用randperm时只接受第二个参数。如果上述对randperm的调用不起作用,请改为以下方式:
rng(123); %// Set seed for reproducibility
numReplicates = 3;
ind = randperm(size(ab,1)); %// Randomly choose nColors colours from data
ind = ind(1:numReplicates*nColors); %// We are also repeating the experiment numReplicates times

%// Make a 3D matrix where each slice denotes the initial centres for each iteration
seeds = permute(reshape(ab(ind,:).', [2 nColors numReplicates]), [2 1 3]);

%// Now call kmeans
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
                                      'Replicates', numReplicates, 'start', seeds);

现在,通过上述代码,您应该能够每次生成相同的颜色分割结果。祝好运!

@user2916044 - 啊,我错过了那个。让我编辑我的代码。Replicates 的意思是你想要重复分割那么多次,每次使用不同的初始点集。让我修改一下。 - rayryeng
@user2916044 - 完成了。这应该能够正常工作。我忘记了Replicates标志。 - rayryeng
非常感谢。我将numReplicates和nColors更改为4,而不改变代码中的其他内容,结果分割效果更好。这样做可以吗?还是我犯了错误? - hs100
1
@user2916044 考虑给像这样的好答案点赞 :) - Divakar
1
@rayryeng 是的,最好的事情是你似乎很享受它!当然,在这个过程中人们也会受益 :) - Divakar
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接