寻找具有零边界的岛屿

时间:2017-08-22 11:46:17

标签: algorithm matlab matrix

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

假设我有这个矩阵:

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行,以及第3列和第3列。

如下所示:

explanation

我想要岛屿的行和列索引。因此,对于上述矩阵,所需的输出是:

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)函数可以给出行的索引和列索引,但是我没有关于如何检测连接索引的线索,因为它们可以以水平,垂直和对角线顺序连接。 关于如何处理这个问题的任何想法?

4 个答案:

答案 0 :(得分:3)

您应该查看regionprops返回的BoundingBox和ConvexHull统计信息:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

<nav class="navbar navbar-default">
      <div class="container-fluid">
        <ul class="nav navbar-nav navbar-right">
          <li class="dropdown">
            <a class="dropdown-toggle" data-toggle="dropdown" href=""><?php echo $surname; ?> <span class="glyphicon glyphicon-user"></span> <span class="caret"></span></a>
            <ul class="dropdown-menu">
              <li><a href=""><span class="glyphicon glyphicon-user"></span> Profile</a></li>
               <li><a href=""><span class="fa fa-unlock"></span> Change Password</a></li>
              <li><a href="">Page 1-3</a></li>
            </ul>
          </li>
          <li><a href="<?php echo base_url('logout'); ?>"><span class="glyphicon glyphicon-log-in"></span> Logout</a></li>
        </ul>
      </div>
    </nav>
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
  <ul class="nav nav-tabs">
    <li class="active"><a data-toggle="tab" href="#home">Home</a></li>
    <li><a data-toggle="tab" href="#menu1">Menu 1</a></li>
    <li><a data-toggle="tab" href="#menu2">Menu 2</a></li>
    <li><a data-toggle="tab" href="#menu3">Menu 3</a></li>
  </ul>

  <div class="tab-content">
    <div id="home" class="tab-pane fade in active">
      <h3>HOME</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
    </div>
    <div id="menu1" class="tab-pane fade">
      <h3>Menu 1</h3>
      <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
    </div>
    <div id="menu2" class="tab-pane fade">
      <h3>Menu 2</h3>
      <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.</p>
    </div>
    <div id="menu3" class="tab-pane fade">
      <h3>Menu 3</h3>
      <p>Eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
    </div>
  </div>
</div>

</body>
</html>

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

答案 1 :(得分:1)

非常简单!

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

答案 2 :(得分:1)

使用图像扩张和regionprops

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);

答案 3 :(得分:1)

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

包装盒

首先是简单的部分。

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
  }
}