【问题标题】:Get the indexes of the boundary cells of a subset of a matrix. Matlab获取矩阵子集的边界单元的索引。 MATLAB
【发布时间】:2012-11-16 23:58:22
【问题描述】:

给定一个矩阵,其中 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

想法?

【问题讨论】:

    标签: matlab matrix octave subset boundary


    【解决方案1】:

    您应该将此视为形态问题,而不是集合论。这可以通过imdilate() 轻松解决(需要图像包)。您基本上只需要使用 1 的 3x3 矩阵将图像减去其dilation

    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 工具箱的情况下进行操作,请查看 source of imdilate() in Octave。对于逻辑矩阵(这是您的情况),这是属于 matlab 核心的 filter2() 的简单用法。也就是说,以下一行应该可以正常工作并且速度更快

    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
    

    【讨论】:

    • 效果很好,我正在编写一个包,并希望它只使用标准 MATLAB,而不是图像包。谢谢!
    • @kpurdon 我写的代码也在 matlab 中以完全相同的方式运行(imdilate() 也是 matlab 图像工具箱的一部分)。
    • 正确! imdilate() 是否包含在基本 matlab 中,而不是图像工具箱是 matlab 的标准,无需额外购买?
    • @kpurdon 你是对的,我误解了你。它需要 matlab 图像工具箱。当您提到标准 Matlab 时,我读到“我想要一些用于 matlab 而不是 Octave 特定的东西”,因此我的回答是它不是 Octave 特定的。无论如何,您可以使用不需要工具箱的filter2()(请参阅我的编辑)
    【解决方案2】:

    一种可能的解决方案是获取子集并将其添加到原始矩阵中,但要确保每次添加它时,将其位置偏移 +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
    

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

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

    【讨论】:

    • 肯定是一个很好的解决方案,但看起来走扩张路线要简单一些。
    • 当然,我很想看看不同的解决方案在计算时间方面的比较。你在处理什么大小的输入矩阵?
    • 输入大小会有所不同。我正在 10x10 和 1000x1000 上进行测试,但我将其编写为理论上可以处理任何尺寸的函数。
    【解决方案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));
    

    【讨论】:

    • 正是我正在寻找的解决方案。我知道一些相当简单的事情正在逃避我。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-09
    • 2016-02-05
    • 1970-01-01
    • 2017-02-24
    • 2018-03-26
    • 2014-12-22
    • 1970-01-01
    相关资源
    最近更新 更多