Update 2: A simpler/faster approach to get the gap length:
ClearAll[hWidth]
hWidth = Composition[Max, Cases[p : {0 ..} :> Length[p]],
Drop[#, #[[-1, 1]] - 1] &, Drop[#, 1 - #[[1, 1]]] &,
Split, Map[Max], Transpose, ImageData, MorphologicalBinarize, ImageCrop];
hWidth /@ {img1, img2, img3}
{548, 17, 61}
or
ClearAll[hWidth2]
hWidth2 = Composition[# - 1 &, Max, Differences,
SparseArray[#]["NonzeroPositions"] &,
Map[Max], Transpose, ImageData, MorphologicalBinarize, ImageCrop];
hWidth2 /@ {img1, img2, img3}
{548, 17, 61}
Original answer:
A three-step procedure:
Full vertical dilation of the input image using Dilation
Selecting components (all rectangles after dilation) using
SelectComponents
and
- Using
ComponentMeasurements
to get the "BoundingBox"
and
"CaliperWidth"
of the selected components
ClearAll[vDilate, internalRectangles, dataSet]
vDilate = ColorNegate @
Dilation[MorphologicalBinarize@#, ConstantArray[1, {ImageDimensions[#][[1]], 1}]] &;
internalRectangles[w_: 1] := Rectangle @@@ Values[
ComponentMeasurements[
SelectComponents[vDilate @ #,
#CaliperWidth > w && #AdjacentBorders == {Bottom, Top} &],
"BoundingBox"]] &;
dataSet[w_:1] := ComponentMeasurements[
SelectComponents[vDilate @ #,
#CaliperWidth > w && #AdjacentBorders == {Bottom, Top} &],
{"BoundingBox", "CaliperWidth"}, "Dataset"] &;
Examples:
{img1, img2, img3} = Import /@
{"https://i.stack.imgur.com/kRjAA.jpg",
"https://i.stack.imgur.com/d0ALv.jpg",
"https://i.stack.imgur.com/W5Zcd.jpg"}

For the first two images we get a single rectangle using the default value (1
) for the caliper width threshold:
dataSet[] @ img1

HighlightImage[#, internalRectangles[]@#] & @ img1

dataSet[] @ img2

HighlightImage[#, internalRectangles[] @ #] & @ img2

For img3
, we get many rectangles with the default caliper width threshold:
dataSet[] @ img3

HighlightImage[#, internalRectangles[]@#] & @ img3

Using a larger value for the threshold caliper width gives a single rectangle:
dataSet[25] @ img3

HighlightImage[#, internalRectangles[25]@#] & @ img3

Alternatively, we can select the rectangle with maximal width from internalRectangles[]@img3
:
HighlightImage[#,
MaximalBy[#[[2, 1]] - #[[1, 1]] &]@(internalRectangles[]@#)] &@img3
same picture
Update: To get the internal rectangle with maximum width in a single step, we can define dataSet
as follows:
ClearAll[dataSet2]
dataSet2 = MaximalBy[#CaliperWidth &] @
ComponentMeasurements[
SelectComponents[vDilate @ #, #AdjacentBorders == {Bottom, Top} &],
{ "BoundingBox", "CaliperWidth"}, "Dataset"] &;
dataSet2 /@ {img1, img2, img3}
