使用MATLAB根据位置对这些进行分组?

时间:2014-04-16 13:06:04

标签: matlab matrix

我在MATALB中有一个带有{0, 1}条目的矩阵。我希望按照以下规则收集成员1

  • 我从矩阵中的任何1开始,然后我将其放入group1
  • 我去后看看相应的行和列中是否有。如果是这样,我会将该行和列中的所有内容添加到group1
  • 每当我在行或列中找到一个时,我就会看到相应的行和列,并将其添加到该组(group1)。
  • 如果我在相应的行或列中找不到任何其他人,我会转到下一组,例如group2
  • 我继续这样,直到矩阵中找不到任何人。

这里我举一个简单的例子:

A = [ 1 0 0 1 0
      1 0 0 0 0
      0 0 0 1 0
      0 0 0 1 0 
      0 1 1 0 0 ]

group1 = {(1, 1)}
% I put in group1 the first element A(1, 1) (arbitrarly)
% Since in row 1 and column 1 we have A(1, 4) = 1 and A(2, 1) = 1 so I 
% add them to group1
group1 = {(1, 1), (1, 4), (2, 1)}
% We see now that I have no one that correspond to the element A(2, 1) since 
% A(2, :) = 0 and A(:, 1) = 0. But I have ones that correspond to element A(1, 4) 
% which are A(3, 4) and A(4, 4) so I add them to group1
group1 = {(1, 1), (1, 4), (2, 1), (3, 4), (4, 4)}
% Also A(3, 4) and A(4, 4) have no corresponding ones. Hence I stop and I go to group2
group2 = {(5, 2)}
% I do the same and I get:
group1 = {(1, 1), (1, 4), (2, 1), (3, 4), (4, 4)}
group2 = {(5, 2), (5, 3)}

我想到的是这样的:

for i = 1:size(A, 1)
   e{i} = find(A(i, :)~=0);
end
for j = 1:size(A, 1)
   for i = e{j}
      a{i} = find(A(:, i)~=0);
   end
end

有什么建议吗?我可以不用循环吗?

非常感谢你的帮助。

3 个答案:

答案 0 :(得分:2)

如果您有图像处理工具箱,一种方法是创建包含给定行或列中所有项的段。以下是矩阵的示例:

首先,用零边框填充数组:

Apad=padarray(A, [1,1], 0, 'both');

然后使用水平和垂直线对此阵列执行形态学闭合:

se1 = strel('line',3,0);
se2 = strel('line',3,90);
closedA = imclose(Apad,se1);
closedA = imclose(closedA,se2);

摆脱边框像素,

Segments=closedA(2:end-1, 2:end-1);

结果是:

Segments =

     1     1     1     1     0
     1     0     0     1     0
     0     0     0     1     0
     0     0     0     1     0
     0     1     1     0     0

然后,您可以提取连接的组件:

CC = bwconncomp(Segments,4);

沿着组循环并识别每个组中包含的A中的像素:

LinIndices=find(A);
for k=1:CC.NumObjects
   Pixels{k}=LinIndices(ismember(LinIndices, CC.PixelIdxList{k}))
end

像素现在分组在数组Pixels中,每个单元格包含给定组中像素的线性索引:

Pixels{1}= [1 2 16 18 19]

Pixels{2}= [10 15]

图形:

A(Pixels{1})=1
A(Pixels{2})=2

A =

     1     0     0     1     0
     1     0     0     0     0
     0     0     0     1     0
     0     0     0     1     0
     0     2     2     0     0

注意结构元素se1se2的大小必须优化到孤立像素之间的最大距离。有关加入细分的替代方法,请参阅this other answer by @Bentoy13

答案 1 :(得分:2)

按照@Jigg的想法,我提出了另一个非常类似的解决方案。

为每个行和列定位第一个和最后一个索引,其中有一个索引。将这两个索引之间的段填充为另一个矩阵中的索引:

B = zeros(size(A));
for i=1:size(A,1)
    m1 = find(A(i,:),1,'first');
    m2 = find(A(i,:),1,'last');
    B(i,m1:m2) = 1;
end
for i=1:size(A,2)
    m1 = find(A(:,i),1,'first');
    m2 = find(A(:,i),1,'last');
    B(m1:m2,i) = 1;
end

(可能有更短的方法来做那件事......)

然后标记新阵列B的所有连接组件,并用阵列A:

对其进行掩码
Result = bwlabel(A,4).*A;

答案 2 :(得分:1)

在过去尝试解决类似的问题时(主要是为了解决某些逻辑谜题),我总是发现最简单的方法是保留一堆你仍然需要查看的点,然后逐个处理它们直到堆栈为空。

写在pseudocode,这很简单:

groups = []
while any_points_left(A):
    group = []
    stack = [pick_random_point(A)]
    while not_empty(stack):
        p = stack.pop()
        group.append(p)
        mark_as_done(p)
        for q in all_points_in_same_row_or_column(p):
            stack.append(q)
    groups.append(group)

翻译成Matlab(这不是这种代码的理想语言):

groups = {};
while nnz(A) > 0 % any points left?
    % find random point
    [i, j] = ind2sub(size(A), find(A, 1, 'first'));
    stack = {[i, j]};
    group = {};
    while ~isempty(stack)
        p = stack{end}; stack = stack(1:end-1); % pop point from stack
        i = p(1); j = p(2);
        if A(i, j) == 0 % check if point is not already removed
            continue
        end
        A(i, j) = 0; % mark as done   
        group{end+1} = p; % add to group

        for ii = 1:size(A, 1) % check same column
            if A(ii, j) == 1
                stack{end+1} = [ii, j]; % add to stack
            end
        end        
        for jj = 1:size(A, 2) % check same row
            if A(i, jj) == 1
                stack{end+1} = [i, jj]; % add to stack
            end
        end
    end
    groups{end+1} = group;
end

% print result
for ig = 1:length(groups)
    fprintf('Group %i: ', ig)
    group = groups{ig};
    for ip = 1:length(group)
        fprintf('(%i, %i) ', group{ip}(1), group{ip}(2))
    end
    fprintf('\n')
end

应该可以以清晰为代价将此代码编写得更紧凑。结果:

Group 1: (1, 1) (1, 4) (4, 4) (3, 4) (2, 1) 
Group 2: (5, 2) (5, 3)