3

给定一个矩阵,其中 1 是当前子集

test =

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

是否有函数或快速方法可以将子集更改为当前子集的边界?

例如。从上面的“测试”中获取这个子集

test =

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

最后,我只想获得围绕矩阵子集的最小单元格。当然,我可以循环并获得边界的最小值(逐个单元格),但必须有一种方法可以使用我上面显示的方法来做到这一点。

请注意,子集将被连接,但可能不是矩形。这可能是一个大问题。

这是一个可能的子集......(会用 NaN 边框填充它)

test =

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

想法?

4

3 回答 3

3

我将使用的基本步骤是:

  1. 对形状执行膨胀以获得一个新区域,即形状加上其边界
  2. 从扩张的形状中减去原始形状,只留下边界
  3. 使用边界来索引您的数据矩阵,然后取最小值。

扩张

我在这里要做的是在每个单元格上传递一个 3x3 窗口并在该窗口中取最大值:

[m, n] = size(A); % assuming A is your original shape matrix
APadded = zeros(m + 2, n + 2);
APadded(2:end-1, 2:end-1) = A; % pad A with zeroes on each side
ADilated = zeros(m + 2, n + 2); % this will hold the dilated shape.

for i = 1:m
    for j = 1:n
        mask = zeros(size(APadded));
        mask(i:i+2, j:j+2) = 1; % this places a 3x3 square of 1's around (i, j)
        ADilated(i + 1, j + 1) = max(APadded(mask));
    end
end

形状减法

这基本上是一个逻辑 AND 和一个逻辑 NOT 来删除交集:

ABoundary = ADilated & (~APadded);

在这个阶段,您可能想要删除我们添加的用于膨胀的边框,因为我们不再需要它了。

ABoundary = ABoundary(2:end-1, 2:end-1);

找到沿边界的最小数据点

我们可以使用我们的逻辑边界将原始数据索引到一个向量中,然后只取该向量的最小值。

dataMinimum = min(data(ABoundary));
于 2012-11-29T01:40:36.920 回答
2

您应该将此视为形态学问题,而不是集合论。这可以很容易地解决imdilate()(需要图像包)。您基本上只需要使用 1 的 3x3 矩阵将图像减去其膨胀。

octave> test = logical ([0  0  0  0  0  0
                         0  0  0  0  0  0
                         0  0  1  1  0  0
                         0  0  1  1  0  0
                         0  0  1  1  1  1
                         0  0  1  1  1  1]);
octave> imdilate (test, true (3)) - test
ans =

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

但是,它不使用 NaN 填充。如果你真的想要,你可以用 false 填充原始矩阵,执行操作,然后检查边框中是否有任何真值。

请注意,您不必使用logical(),在这种情况下您必须使用ones()而不是true(). 但这需要更多的内存并且性能更差。

编辑:由于您尝试在不使用任何 matlab 工具箱的情况下执行此操作,因此请查看Octave的来源imdilate()。对于逻辑矩阵(这是您的情况),这是filter2()属于matlab核心的简单用法。也就是说,以下一行应该可以正常工作并且要快得多

octave> (filter2 (true (3), test) > 0) - test
ans =

   0   0   0   0   0   0
   0   1   1   1   1   0
   0   1   0   0   1   0
   0   1   0   0   1   1
   0   1   0   0   0   0
   0   1   0   0   0   0
于 2012-11-29T01:39:35.547 回答
1

一种可能的解决方案是获取子集并将其添加到原始矩阵,但确保每次添加它时,将其位置偏移 +1 行、-1 行和 +1 列、-1 列。然后,结果将在原始子集周围扩展一行和一列。然后,您使用原始矩阵将原始子集掩码为零。

像这样:

test_new = test + ...
[[test(2:end,2:end);zeros(1,size(test,1)-1)],zeros(size(test,1),1)] + ... %move subset up-left
[[zeros(1,size(test,1)-1);test(1:end-1,2:end)],zeros(size(test,1),1)] + ... %move down-left
[zeros(size(test,1),1),[test(2:end,1:end-1);zeros(1,size(test,1)-1)]] + ... %move subset up-right
[zeros(size(test,1),1),[zeros(1,size(test,1)-1);test(1:end-1,1:end-1)]];  %move subset down-right

test_masked = test_new.*~test; %mask with original matrix
result = test_masked;
result(result>1)=1; % ensure that there is only 1's, not 2, 3, etc.

您的test矩阵上的结果是:

result =

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

已编辑 - 它现在也可以通过将子集向上和向左、向上和向右、向下然后向左和向下然后向右移动来抓取角落。

我希望这将是实现这一目标的一种非常快速的方法——它没有任何循环,也没有函数——只有矩阵运算。

于 2012-11-29T01:40:09.677 回答