5
$\begingroup$

I recorded the following image (28$\times$28 pixels) showing a bright square object (I used a square object of 35 micrometers side length and imaged it with a microscope):

enter image description here

Scaled to pixel size (for better visualization) it looks the following:

enter image description here

How can I determine the side length?

I started with:

data = ImageData[image, "Byte"];
ListLinePlot[data[[All, 14]], ImageSize -> Medium]

which gives:

enter image description here

and in 3d:

ListPlot3D[ImageData[image,"Byte"]]

enter image description here

Now I could determine the mean position of the strongest slope at both edges and determine from that the side length.

How would you proceed? Are there image analysis functions existing which can be used to detect the edges to subpixel resolution?

$\endgroup$
4
  • 1
    $\begingroup$ What do you already know about the shape? Can we assume that it is a rectangle? Can we assume that it is in fact a special rectangle: a square? Can we assume that the sides are aligned with the sides of the image? A recognition/measurement algorithm could potentially make use of all this information. $\endgroup$
    – Szabolcs
    Mar 7, 2016 at 11:48
  • 1
    $\begingroup$ Maybe I don't understand the question properly, but as I understand it I would have written {First[#], Last[#], First[#] - Last[#]} &@ Ordering@Differences[data[[All, 13]]] to compute the required side lengths from the points of strongest slope. $\endgroup$
    – C. E.
    Mar 7, 2016 at 11:51
  • $\begingroup$ @Szabolcs: Thank you for this important questions. Yes the object is a square (same height and width of 35 micrometers). The lines of the square are experimentally aligned with the sides of the image (as good as possible, but there might be a small invisible rotation angle). The square has in reality a uniform constant white color. $\endgroup$
    – mrz
    Mar 7, 2016 at 12:33
  • $\begingroup$ @C.E. Thank you. This is very elegant solution to find the positions of highest negative and positive slope and from that the side length. The accuracy of your proposal is 1 pixel. Since the object is a square I could use your proposal and then calculate the mean of all possible (smoothed) curves to get subpixel resolution. $\endgroup$
    – mrz
    Mar 7, 2016 at 13:12

4 Answers 4

2
$\begingroup$
data = ImageData[i1]; 
s = Select[Split@MaxDetect[GradientFilter[#, 2], 1/100] & /@ 
                                  Join[data, Transpose@data], Length@# == 5 &];
Length[#[[2]]/2] + Length@#[[3]] + Length[#[[4]]/2] & /@ s // Mean // N

(* 19.878 *)
$\endgroup$
6
  • $\begingroup$ Excellent ... is it possible for this code to give information about the measurement error (e.g. standard deviation)? $\endgroup$
    – mrz
    Mar 7, 2016 at 14:59
  • 1
    $\begingroup$ @mrz Length[#[[2]]/2] + Length@#[[3]] + Length[#[[4]]/2] & /@ s // StandardDeviation // N $\endgroup$ Mar 7, 2016 at 15:08
  • $\begingroup$ @mrz But perhaps you could make some better outlier treatment given the measurements distribution Length[#[[2]]/2] + Length@#[[3]] + Length[#[[4]]/2] & /@ s // Histogram $\endgroup$ Mar 7, 2016 at 15:10
  • 1
    $\begingroup$ @mrz I believe the point length = 25 is a clear outlier $\endgroup$ Mar 7, 2016 at 15:15
  • 1
    $\begingroup$ @mrz I believe the following is more precise {#[[2]] + 14 - #[[1]]} & /@ Map[Ordering[-#, 1] &, Partition[#, 14] & /@ Select[(GradientFilter[#, 1]) & /@ Join[data, Transpose@data], Max@# > .05 &], {2}] // Mean // N gives 18.125 $\endgroup$ Mar 7, 2016 at 15:35
4
$\begingroup$

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}]}
 ]

Mathematica graphics

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.

$\endgroup$
1
  • $\begingroup$ Thank you for this solution ... I will try to compare it to the other codes in the next days. $\endgroup$
    – mrz
    Mar 8, 2016 at 20:58
3
$\begingroup$

This estimates the side length at nearly 21:

edges = EdgeDetect[image];
ComponentMeasurements[
 WatershedComponents[Closing[edges, 0.5], CornerNeighbors -> False],
 "Area"]
Sqrt[1 /. %]    (* using area to estimate a square *)
(*
  {1 -> 437.375, 2 -> 283.375}
  20.9135
*)

This is what edges looks like:

Mathematica graphics

$\endgroup$
2
  • $\begingroup$ This is a very interesting solution, thank you ... Could you estimate the error as Dr. belisarius did? $\endgroup$
    – mrz
    Mar 7, 2016 at 22:10
  • 2
    $\begingroup$ @mrz The component measurement of the area of the edge, which can be found with ComponentMeasurements[1 + WatershedComponents[Closing[edges, 0.5], CornerNeighbors -> False], "Area"], shows the uncertainty in the area to be on the order of 66.75, which from $dA = 2s \, ds$ translates to an uncertainty in side length of a little over 1.5 pixels. I'm not sure I understand Dr. bel's analysis, since it seems that in the image the corners are naturally cut off. This means the average cross-section would underestimate the side. But you can apply his code to the image Closing[edges, 0.5]. $\endgroup$
    – Michael E2
    Mar 8, 2016 at 3:55
2
$\begingroup$
pic = Import["https://i.stack.imgur.com/2uAtN.png"]
Mean @@ Abs[
  Subtract @@@ 
   Values[ComponentMeasurements[MorphologicalBinarize[pic], 
     "BoundingBox"]]]

19.5

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.