Finding complete rectangles enclosing 0 Finding complete rectangles enclosing 0 arrays arrays

Finding complete rectangles enclosing 0


This is quite simple. If you have n squares, you can count the rectangles in O(n).

Assumptions:

  • The borders of each valid rectangle don't share any cells with a non-valid path.
  • If one rectangle is inside another, you would be happy finding them

You would need extra memory as big as the input. Let's call this visited and initialize with 0.

Let's first construct a helper function:

is_rectangle(square s)    from s, go right while you see 1s and visited is 0        mark them as 1 in visited    if less than 3 steps        return 0    then, go down while you see 1s and visited is 0        mark them as 1 in visited    if less than 3 steps        return 0    then, go left while you see 1s and visited is 0        mark them as 1 in visited    if less than 3 steps        return 0    then, go up while you see 1s and visited is 0        mark them as 1 in visited    if then one above is not s        return 0    return 1

This function basically traces the 1s in the directions of right-down-left-up and checks if the conditions are met (length at least 3 and reaching the starting position). It also marks the visited squares.

The important thing to notice about this function, is that it works correctly only if the initial square is the top-left corner.

Now, the solution to the problem is easy:

num_rectangles = 0initialize visited to 0 (rows x columns)for r in rows    for c in columns        if visitied[r][c] or image[r][c] == 0            continue        num_rectangles += is_rectangle(<r,c>)return num_rectangles

Here is how the algorithm executes:

enter image description here
1. Failed (and marked) part of a bad rectangle

enter image description here
2. Found (and marked) a rectangle.

enter image description here
3. Failed on a single square (of a vertical line)

enter image description here
4. Failed on a single square (of a vertical line)

enter image description here
5. Failed on a single square (of a vertical line)

enter image description here
6. After many similar steps, found the next rectangle.

enter image description here
7. And the next rectangle

enter image description here
8. Algorithm end


The following O(n) algorithm will work on any 2D matrix of 0/1 values (that is, intersecting/overlapping rectangles are allowed, as are arbitrary non-rectangular open/closed shapes). The definition of a rectangle I'm using here is "the interior is entirely made up of 0-cells" (so e.g. if one rectangle entirely contains another, only the inner rectangle will be found; if containing rectangles should also be considered, then each found rectangle can be deleted and the algorithm restarted). It's based on the observation that each 0-cell can be in the interior of at most one 1-rectangle.

I use the convention that x = 0 is the leftmost position and y = 0 is the topmost position.

  1. Find the top-left corner. Starting at the top-left cell and proceeding left-to-right and top-to-bottom, find the next unvisited cell that could be the top-left corner of a solid-0 rectangle: specifically it must be a 0-cell that has 1-cell neighbours in the SW, W, NW, N and NE positions, and 0-cells in the remaining 3 neighbouring positions.
  2. Find the top-right corner. Scan through neighbours to its right while these cells are 0 and have a 1-cell N neighbour.
  3. Could this be the top row of a solid 0-rectangle? If the the last cell found by the above loop before it ends is a cell that could be the top-right cell in a solid-0 rectangle (specifically a 0-cell having 1-cell neighbours in the NW, N, NE, E and SE cells, and 0-cells in the remaining 3 positions), then we have established both the top y co-ordinate and the exact width of the only possible solid 0-rectangle that uses any of these cells. If the last cell doesn't meet these top-right-corner conditions, then none of these cells can be part of a solid 0-rectangle: mark them visited and goto 1.
  4. Call the start and end x co-ordinates of the strip of 0-cells x1 and x2; call the vertical position y1.
  5. Scan down, a row at at time. Set y2 = y1, and while the line between x1 and x2 at vertical position y2 could be part of the solid 0-rectangle, increment y2. Specifically, the test at each vertical position y2 is: the cells at (x1 - 1, y2) and (x2 + 1, y2) must both be 1, and all cells in between must be 0.
  6. Could this be the bottom row of a solid 0-rectangle? If the last row found by the previous loop before it ends is a row that could be the bottom row of a solid 0-rectangle (specifically there are 1-cells all the way from (x1 - 1, y2 + 1) to (x2 + 1, y2 + 1)), then we have found a complete solid 0-rectangle surrounded by 1-cells: if its size is greater than the biggest discovered so far, then record it as the new biggest rectangle. Otherwise (if there is not a solid row of 1-cells in the next line down), none of the 0-cells examined can be part of any solid 0-rectangle: mark them all as visited and goto 1.


If you can have only rectangular shapes in your array, it is equivalent to a classic problem of computation on binary images: just apply a standard algorithm for connected components. You label only connected components of 0's, and count them.

See http://en.wikipedia.org/wiki/Connected-component_labeling for example. This type of algorithm is quite simple on images but take some amount of memory (same size as your input array, of type short or int). Be careful of connectivity: if you choose 4-connectivity, you will count enclosed rectangles even if some corners are missing. But the algorithm is simpler than with 8-connectivity.

If you can have more complex enclosed shapes, just add a post-processing: for each connected component, count the number of pixels inside the bounding box of the component (if the two numbers are equal, you have a rectangle)