Here's my take at the issue (some comments are inlined)
extractRectangles[i_Image?ImageQ] :=
Block[
{dims, i0, i1, masks1, masks2, masks3, tfun, coords},
(* downsampling (just to make it faster) *)
i0 = ImageResize[RemoveAlphaChannel[i], 500];
dims = ImageDimensions[i0];
(* smoothing *)
i1 = MeanShiftFilter[i0, 3, .05, MaxIterations->5];
(* color based segmentation *)
masks1 = DominantColors[i1, All, "CoverageImage",
MinColorDistance -> .02,
ColorCoverage -> .02
];
(* mask cleaning *)
masks2 = FillingTransform @ Opening[#,2] & /@ masks1;
(* mask selection *)
masks3 = Flatten[Map[
Values@ComponentMeasurements[
#, "BoundingBox",
And[#Rectangularity > .5, #Area > .005 Times @@ dims] &
]&, masks2], 1];
(* scaling to the original image size *)
tfun = ScalingTransform[ImageDimensions[i] / dims];
coords = tfun /@ masks3;
(* rectangles *)
Rectangle @@@ coords
]
Compared to your attempt I used a MeanShiftFilter
in place of the MeanFilter
to better preserve the hard edges and DominantColors
to get a more controlled colour segmentation.
Once that's done I want to have just one more utility to get consistent colouring for the rectangles
exprColor[expr_] :=
RGBColor["#" <> IntegerString[Hash[Unevaluated[expr]], 16, 6]]
And now this is the result.
res = extractRectangles /@ examples;
MapThread[
Function[{image, rectangles},
HighlightImage[
image, {{"Boundary", 10}, exprColor[#], #} & /@ rectangles,
ImageSize -> 400]
],
{examples, res}
] // Column
The main issues I have still are:
- it gets thrown off by the text and thus some rectangles are smaller than they should be
- it does not deals very well with nested rectangles
- it has no notion of overlapping rectangles
Unfortunately, I don't have much time to dig into this more in depth.
Cheers!
"Rectangularity"
properties fromSelectComponents
orComponentMeasurements
may be of interest $\endgroup$