【问题标题】:Finding islands of ones with zeros boundary寻找边界为零的岛屿
【发布时间】:2017-08-22 11:46:16
【问题描述】:

我正在尝试在矩阵中找到数字孤岛。 我所说的岛是指一个矩形区域,其中一个在水平、垂直或对角线上相互连接,包括零的边界层

假设我有这个矩阵:

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

边界层是指第 2 行和第 7 行,以及岛#1 的第 3 列和第 10 列。

如下图所示:

我想要岛屿的行和列索引。所以对于上面的矩阵,想要的输出是:

isl{1}= {[2 3 4 5 6 7];          % row indices of island#1
       [3 4 5 6 7 8 9 10]}       % column indices of island#1

isl{2}= {[2 3 4 5 6 7];          % row indices of island#2
       [12 13 14 15 16 17]};     % column indices of island#2

isl{3} ={[9 10 11 12];           % row indices of island#3
       [2 3 4 5 6 7 8 9 10 11];} % column indices of island#3

先检测到哪个岛并不重要。

虽然我知道[r,c] = find(matrix) 函数可以给出行和列索引,但我不知道如何检测连接的索引,因为它们可以按水平、垂直和对角线顺序连接。 关于如何处理这个问题的任何想法?

【问题讨论】:

  • 检查所有方面。如果它也包含 1,则查找下一个 1,依此类推。
  • 由于我对matlab不太熟悉,所以不能给你好的代码。但是,您可以创建一个集合,其中包含任何发生的边界周围的所有边界,然后合并所有重叠的边界,从而相应地扩展边界。
  • @Chris 如果您可以建议,可以使用伪代码
  • 合并边界不起作用。两个岛的边界可能相互重叠,但它们仍然是两个独立的岛。
  • 感谢您的澄清。然而,他们让我对“岛 1”感到困惑。有一个被0包围的1(第6行第4列),这不应该是“岛4”吗?

标签: algorithm matlab matrix


【解决方案1】:

您应该查看 regionprops 返回的 BoundingBox 和 ConvexHull 统计数据:

a = imread('circlesBrightDark.png');
bw = a < 100;
s = regionprops('table',bw,'BoundingBox','ConvexHull')

https://www.mathworks.com/help/images/ref/regionprops.html

【讨论】:

  • 您能否解释一下我的矩阵的用法以获得所需的结果?
【解决方案2】:

找到连接的组件及其边界框是很容易的部分。更困难的部分是将边界框合并为岛屿。

边界框

首先是简单的部分。

function bBoxes = getIslandBoxes(lMap)
   % find bounding box of each candidate island
   % lMap is a logical matrix containing zero or more connected components
   bw = bwlabel(lMap);   % label connected components in logical matrix
   bBoxes = struct2cell(regionprops(bw, 'BoundingBox'));   % get bounding boxes
   bBoxes = cellfun(@round, bBoxes, 'UniformOutput', false);   % round values
end

这些值是四舍五入的,因为regionprops 返回的边界框位于其各自组件之外的网格线上,而不是单元格中心,我们需要整数值用作矩阵的下标.例如,一个看起来像这样的组件:

0   0   0
0   1   0
0   0   0

会有一个边界框

[ 1.5000   1.5000   1.0000   1.0000 ]

我们四舍五入

[ 2  2  1  1]

合并

现在是困难的部分。一、合并条件:

  • 如果b2b1(包括边界层)有非零交点,我们将边界框b2 合并到边界框b1 中。

当一个组件全部或部分位于另一个组件的边界框内时,此条件确保边界框被合并,但当一个边界框位于另一个边界框的零边界内时,它也会捕获边缘情况。一旦所有的边界框都合并了,就保证它们有一个全为零的边界(或与矩阵的边缘接壤),否则其边界中的非零值将被合并。

由于合并涉及删除合并的边界框,因此循环是向后完成的,因此我们最终不会索引不存在的数组元素。

不幸的是,通过数组比较每个元素与所有其他元素是不足以捕捉所有情况的。为了表明所有可能的边界框都已合并到岛中,我们使用了一个名为 anyMerged 的标志并循环,直到我们完成一个完整的迭代而不合并任何内容。

function mBoxes = mergeBoxes(bBoxes)
   % find bounding boxes that intersect, and merge them
   mBoxes = bBoxes;
   % merge bounding boxes that overlap
   anyMerged = true;   % flag to show when we've finished
   while (anyMerged)
      anyMerged = false;   % no boxes merged on this iteration so far...
      for box1 = numel(mBoxes):-1:2
         for box2 = box1-1:-1:1
            % if intersection between bounding boxes is > 0, merge
            % the size of box1 is increased b y 1 on all sides...
            %    this is so that components that lie  within the borders
            %    of another component, but not inside the bounding box,
            %    are merged
            if (rectint(mBoxes{box1} + [-1 -1 2 2], mBoxes{box2}) > 0)
               coords1 = rect2corners(mBoxes{box1});
               coords2 = rect2corners(mBoxes{box2});

               minX = min(coords1(1), coords2(1));
               minY = min(coords1(2), coords2(2));
               maxX = max(coords1(3), coords2(3));
               maxY = max(coords1(4), coords2(4));

               mBoxes{box2} = [minX, minY, maxX-minX+1, maxY-minY+1];   % merge
               mBoxes(box1) = [];   % delete redundant bounding box

               anyMerged = true;   % bounding boxes merged: loop again
               break;
            end
         end
      end
   end
end

合并函数使用一个小型实用函数,将[x y width height] 格式的矩形转换为左上角、右下角[x1 y1 x2 y2] 的下标向量。 (这实际上在另一个函数中用于检查岛屿是否有零边界,但如上所述,此检查是不必要的。)

function corners = rect2corners(rect)
   % change from rect = x, y, width, height
   %       to corners = x1, y1, x2, y2
   corners = [rect(1), ...
              rect(2), ...
              rect(1) + rect(3) - 1, ...
              rect(2) + rect(4) - 1];
end

输出格式和驱动函数

mergeBoxes 的返回值是矩形对象的元胞数组。如果您觉得这种格式有用,您可以在这里停下来,但很容易获得请求的格式,其中包含每个岛的行和列范围:

function rRanges = rect2range(bBoxes, mSize)
   % convert rect = x, y, width, height to
   %        range = y:y+height-1; x:x+width-1
   % and expand range by 1 in all 4 directions to include zero border,
   % making sure to stay within borders of original matrix
   rangeFun = @(rect) {max(rect(2)-1,1):min(rect(2)+rect(4),mSize(1));...
                       max(rect(1)-1,1):min(rect(1)+rect(3),mSize(2))};
   rRanges = cellfun(rangeFun, bBoxes, 'UniformOutput', false);
end

剩下的就是将所有其他函数联系在一起的主要函数,我们就完成了。

function theIslands = getIslandRects(m)
   % get rectangle around each component in map
   lMap = logical(m);

   % get the bounding boxes of candidate islands
   bBoxes = getIslandBoxes(lMap);

   % merge bounding boxes that overlap
   bBoxes = mergeBoxes(bBoxes);

   % convert bounding boxes to row/column ranges
   theIslands = rect2range(bBoxes, size(lMap));

end

这是使用问题中给出的示例矩阵的运行:

M =
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   1   1   0   0   0   0   0   0   0   0   1
   0   0   0   1   1   1   0   1   1   0   0   0   1   1   1   1   0
   0   0   0   0   0   0   1   0   1   0   0   0   0   1   1   1   1
   0   0   0   1   0   1   0   1   1   0   0   0   1   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   1   0   1   0   1   1   1   0   0   0   0   0   0   0
   0   0   1   0   1   1   1   1   1   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
>> getIslandRects(M)
ans =
{
  [1,1] =
  {
    [1,1] =
        9   10   11   12
    [2,1] =
        2    3    4    5    6    7    8    9   10   11
  }
  [1,2] =
  {
    [1,1] =
       2   3   4   5   6   7
    [2,1] =
        3    4    5    6    7    8    9   10
  }
  [1,3] =
  {
    [1,1] =
       2   3   4   5   6   7
    [2,1] =
       12   13   14   15   16   17
  }
}

【讨论】:

    【解决方案3】:

    很简单!

    只需使用 bwboundaries 即可获取每个 blob 的边界。然后,您可以在每个边界的每个 xy 方向上获取 minmax 来构建您的盒子。

    【讨论】:

    • 我不知道为什么它被低估了,因为它让我非常接近。然而,有了这个,也有一些错误的检测。 B = bwboundaries(matrix); 正确检测到三个岛(没有可以稍后添加的边界层),但也有三个错误检测。你能建议如何避免这种情况吗?
    • @Likeunknown 我不知道为什么它被否决了 nop .....您可以使用第二个选项,即连接性以确保对角线中的对象被认为是同一个对象。但是,在您的第一个岛上只有 2 个“1”。我不知道使它们成为岛 1 的一部分的规则是什么。也许你需要一个小检查来说明“如果这个岛的边界完全在另一个岛内,那么它就是另一个岛的一部分”。这将需要一个 for 循环和一些 if,但不应该太难
    【解决方案4】:

    使用图像膨胀和区域属性

    mat = [...
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
    0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1;
    0 0 0 1 1 1 0 1 1 0 0 0 1 1 1 1 0;
    0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 1 1;
    0 0 0 1 0 1 0 1 1 0 0 0 1 0 0 0 0;
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;
    0 0 0 1 0 1 0 1 1 1 0 0 0 0 0 0 0;
    0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 0 0;
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0];
    mat=logical(mat);
    dil_mat=imdilate(mat,true(2,2)); %here we make bridges to 1 px away ones
    l_mat=bwlabel(dil_mat,8);
    bb = regionprops(l_mat,'BoundingBox');
    bb = struct2cell(bb); bb = cellfun(@(x) fix(x), bb, 'un',0);
    isl = cellfun(@(x) {max(1,x(2)):min(x(2)+x(4),size(mat,1)),...
                        max(1,x(1)):min(x(1)+x(3),size(mat,2))},bb,'un',0);
    

    【讨论】:

    • 扩张的缺点是如果两个区域被一行或一列零分隔,您可以将它们连接成一个区域。
    • 如果你看到 island#1 的图片,左下角有一个未连接的(1px 间隙)像素。我认为岛屿间隔至少应该是 2 px
    • 不过最后一个字符串可以简化,边界框不能超过图片限制
    • 根据描述,岛屿应该是8连通的最近邻。我怀疑错误 1 ​​应该是它自己的岛屿。
    • 如果是这样,就不需要膨胀,是的。另一个变化是 bb = cellfun(@(x) fix(x) + [-1 -1 1 1], bb, 'un',0);我总是更喜欢视觉示例而不是文本描述
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多