Image processing: Floor plan - detecting rooms' borders (area) and room names' texts

EDIT: Updated the algorithm, new part below

Fun problem!

Ok, my first would be to find the room centers. This is relatively easy using a distance transform and geodesic erosion.

distTransform = DistanceTransform[ColorNegate@m];
ImageAdjust[distTransform]

Mathematica graphics

The distance transform image contains for every pixel the distance to the closest wall. We're looking for the bright "peak" in the distance transform, i.e. the center of each room.

centerAreas = ImageDifference[
    GeodesicDilation[Image[ImageData[distTransform] - 10], 
    distTransform], distTransform]

EDIT: The next part is new

With this, we can use a watershed transform to find the rooms. The watershed transform (intuitively speaking) finds "basins" in a 3d landscape. We'll invert the distance transform image to turn the "peaks" into "basins" and use the room centers as markers:

watershed = 
 DeleteSmallComponents[
  DeleteBorderComponents[
   Binarize[
    Image[WatershedComponents[ColorNegate[distTransform], 
      centerAreas]]]], 1000]

Mathematica graphics

This segments the rooms quite well. Unfortunately, the watershed transform ignores the walls - the components we found are too big. But they're close enough that this simple "grow the room rectangle until it hits the wall"-algorithm finds the actual room areas:

rooms = ComponentMeasurements[watershed, "BoundingBox"];

Clear[growRect]
growRect[{{x1_, y1_}, {x2_, y2_}}] := 
 Module[{checkRectEmpty, growSingleDirection, growSingleStep, cx, cy, 
   left, top, right, bottom, sizeEstimate, size},
  (
   {cx, cy} = Round[{x1 + x2, y1 + y2}/2];

   checkRectEmpty[{left_, top_, right_, bottom_}] := 
    Max[ImageValue[
       m, {cx - left ;; cx + right, cy - top ;; cy + bottom}]] == 0;
   growSingleDirection[size_, grow_] := 
    If[checkRectEmpty[size + grow], size + grow, size];
   growSingleStep[size_] := 
    Fold[growSingleDirection, size, IdentityMatrix[4]];

   sizeEstimate = 
    Abs[Round[{x2 - x1, y2 - y1, x2 - x1, y2 - y1}/2 - 20]];
   {left, top, right, bottom} = 
    FixedPoint[growSingleStep, sizeEstimate, 20];
   Rectangle[{cx - left, cy - top}, {cx + right, cy + bottom}]
   )]

Using this, all that's left is to display the results:

finalRectangles = growRect /@ rooms[[All, 2]];

feetAndInch[n_] := ToString[Round[n/12]] <> "'" <> ToString[Mod[n, 12]]
Show[m,
 Graphics[
  {
   finalRectangles[[ ;; ]] /. 
    rect : Rectangle[{x1_, y1_}, {x2_, y2_}] :>
     {
      {EdgeForm[Red], Transparent, rect},
      {Red, 
       Text[StringForm["`` x ``\n``", feetAndInch@(x2 - x1), 
         feetAndInch@(y2 - y1), (x2 - x1)*(y2 - y1)/(144.)], {x1 + x2,
           y1 + y2}/2]}
      }
   }]]

Mathematica graphics

or, using the original floor plan as background:

Mathematica graphics


img = Import["http://i.stack.imgur.com/qDhl7.jpg"];
nsc = DeleteSmallComponents[Binarize[img, {0, .2}]];
iD = ImageDimensions[img];
mWS = 70; (*Max window span*)
sLT = 5;(*Straight line tolerance*)
i4 = Thinning@(i1 = Closing[MorphologicalTransform[nsc, {"Min", "Max"}], 3])

Mathematica graphics

Now let's find the endpoints for each door and window by using HitMissTransform[] . We define some masks first:

k1 = ConstantArray[-1, 8];
v = {k1, {-1, -1, 0, 0, 1, 0, -1, -1}};
h = {k1[[;; 4]], {-1, -1, 1, 1}, {-1, -1, 0, 0}};

Now we find the candidate endpoints:

p = Position[ImageData@HitMissTransform[i4, #], 1] & /@ {v, Reverse@v, h, Reverse /@ h};
ListPlot[(Union @@ p) /. {a_, b_} -> {b, iD[[2]] - a}, Axes -> False, Frame -> True]  

Mathematica graphics

Now we select only paired endpoints:

lin = Union[
 Select[Tuples[p[[1;;2]]], 0 < #[[1,1]] - #[[2,1]] < mWS && Abs[#[[1,2]] - #[[2,2]]] < sLT &],
 Select[Tuples[p[[3;;4]]], 0 < #[[1,2]] - #[[2,2]] < mWS && Abs[#[[1,1]] - #[[2,1]]] < sLT &]] /. 
                                     {{a_, b_}, {c_, d_}} -> {{b, iD[[2]] - a}, {d, iD[[2]] - c}};
Graphics[{Point@(Union @@ lin), Line@lin}, 
          PlotRange -> iD /. {x_, y_} -> {{1, x}, {1, y}}, Axes -> False, Frame -> True]

Mathematica graphics

We finally "close" the doors and windows and use MorphologicalComponents[] to identify the rooms:

ColorNegate@ImageSubtract[img, Colorize@MorphologicalComponents@ Binarize@
            Erosion[Show[ColorNegate@i1, 
                   Graphics[Line@lin, PlotRange -> iD /. {x_, y_} -> {{1, x}, {1, y}}]], 1]]

Mathematica graphics


first of all, this is a very interesting problem. I cannot provide you with a complete answer, however, if you apply

m = MorphologicalTransform[nsc, {"Max", "Min", "Max"}]

you can use

(mc = MorphologicalComponents[ColorNegate@m]) // Colorize

to find a fair number of the rooms. The expressions

lines = ImageLines[m, Method -> "RANSAC", MaxFeatures -> 15];
Show[m, Graphics[{Thickness[0.01], Blue, Line /@ lines}]]

actually let you find the walls. ImageLines has more options, so

lines = ImageLines[m, Method -> "Hough", "Segmented" -> True];
Show[m, Graphics[{Thickness[0.01], Blue, Line /@ lines}]]

can restrict the search to walls inside the house.