Another approach: Use UnitBox
to model a rectangle or square and fit it to the data:
model = a UnitBox[(x - x0)/b, (y - y0)/c] + d; (* b x c rectangle *)
model = a UnitBox[(x - x0)/b, (y - y0)/b] + d; (* b x b square *)
The results look something like this:
Show[
ListPlot3D[data],
Plot3D[{Indeterminate, model /. fit}, {x, 1, 2 xm}, {y, 1, 2 ym}],
PlotRange -> {0, Max[{data[[All, 3]], model /. {x -> x0, y -> y0} /. fit}]}
]
The rectangular model produces a nearly square estimate of average side length just over 18 pixels.
data = Flatten[
MapIndexed[Flatten[{#2, #1}] &,
ImageData[Binarize[image, 0.4]; ImageAdjust@image, "Byte"], {2}],
1];
{xm, ym} = ImageDimensions[image]/2;
zm = Min[data];
a0 = -Subtract @@ MinMax[data];
model = a UnitBox[(x - x0)/b, (y - y0)/c] + d;
fit = FindFit[data, model,
{{a, a0/2}, {b, xm + 0.01}, {c, xm - 0.01}, {d, zm - 1.1},
{x0, xm + 0.01}, {y0, xm - 0.1}},
{x, y},
Method -> "PrincipalAxis"]
(* {a -> 162.374, b -> 17.9927, c -> 18.3605, d -> 31.1348,
x0 -> 13.8496, y0 -> 15.3473} *)
Sqrt[b*c] /. fit
(* 18.1757 *)
The square model produces an estimate b
of almost 18.
model = a UnitBox[(x - x0)/b, (y - y0)/b] + d;
fit = FindFit[data, model,
{{a, a0}, {b, xm + 0.01}, {d, zm - 0.1},
{x0, xm + 0.01}, {y0, xm - 0.1}},
{x, y},
Method -> "PrincipalAxis"]
(* {a -> 162.374, b -> 17.9961, d -> 31.1348, x0 -> 13.8987, y0 -> 15.0648} *)
Note that since the data is discrete and UnitBox
is piecewise constant, small changes in the parameters related to the domain, b
, c
, x0
and y0
, do not change the goodness of the fit.
{First[#], Last[#], First[#] - Last[#]} &@ Ordering@Differences[data[[All, 13]]]
to compute the required side lengths from the points of strongest slope. $\endgroup$