10
$\begingroup$

I am new to Mathematica, and I am trying measure length and diameter of rectangular objects in an image. I wanted to fit rectangles in the objects, and then get the values.

Can I do that with ComponentMeasurements? how does the {"Rectangle"} command work? How can I set aspect ratio, or verticality selection rules? or other types conditions to rectangle fit?

What I did so far is as follow:

image = Import["https://i.stack.imgur.com/5WGzV.jpg"]

image of some objects

imageNS = ImageCrop[image, {1024, 650}, Bottom]

so far I just removed the bar at the bottom.

imageA = ImageMultiply[imageNS, 2.8] // ImageAdjust;
imageB = MeanShiftFilter[imageA, 5, 0.03, MaxIterations -> 20] // ImageAdjust;

in this way I have enhanced the contrast of the image.

binaB = MorphologicalBinarize[imageB, {0.4, 0.7}];

Then I binarize the image. since in the binarized image there is no bottom line for the "trees", I try to find it as follows:

imageR = RidgeFilter[imageB, 1.5] // ImageAdjust;
binaE = MorphologicalBinarize[imageR, {0.075, 0.7}];
a = EdgeDetect[binaE, "StraightEdges" -> 0.55];
b = Closing[a, 6];
c = GradientFilter[b, {{1, 100}, 10}];
d = Binarize[ColorNegate[Dilation[c, {{1}, {1}, {1}, {1}, {1}}]]];
ebin = Closing[ImageMultiply[binaB, d], 1]

Now I have the bottom line of the trees, although the problem is that morphologically the trees still belong to the same domain, they are all "connected". This makes the problem more complicated since ComponentMeasurements[ebin,"BoundingBox"] fit bigger rectangles that groups bunches of trees...

Any idea how to fit these rectangles into the trees?

I have tried a different approach to get at least the length of these structures.I use a derivative filter and horizontal erosion to get the top of the structures. Then I get the position of the top of the strcuture with Pixelvalue positoin, get the y coordinate, and subtract it to the bottom line.

qr = Binarize[Erosion[DerivativeFilter[ebin, {1, 0}], {{1, 1, 1, 1}}]]

get the max points positions

ebinm = PixelValuePositions[qr, "Max"];
ebinmy = Table[ebinm[[i]][[2]], {i, 1, Length[ebinm]}];

sum the raws of the matrix to get the baseline

ebinmm = Total[ImageData[qr], {2}];

get the baseline position

baseline = Position[ebinmm, Max[ebinmm]] [[1]][[1]];

get the length of the nws by doing the difference between

lenghts = Subtract[baseline, ebinmy];
Histogram[lenghts]

Still, how do I get the diameters?

I found a nice approach in: Image processing: Floor plan - detecting rooms' borders (area) and room names' texts

I tried to implement it as follows:

distTransform = DistanceTransform[ebin] // ImageAdjust;
centerAreas = ImageDifference[GeodesicDilation[distTransform, ebin],distTransform];
watershed = ImageMultiply[Dilation[WatershedComponents[centerAreas] // Image,{{1},{1}}],ColorNegate[qr]]
rooms = ComponentMeasurements[watershed, "BoundingBox"];

Although the fact that the trees are not individually morphologically defined gives me troubles, and no rectangles are fit into it.This is what I get: enter image description here

Anybody able to fit rectangles into it?

Cheers!

$\endgroup$
6
  • 1
    $\begingroup$ CaliperLength and CaliperWidth give the width/height of the smallest (possibly rotated) bounding rectangle, IIRC. $\endgroup$ Aug 20, 2013 at 9:13
  • $\begingroup$ in my case all the rectangles are mostly parallel between each other and oriented vertically. It looks like a comb more or less. I tried to use: ComponentMeasurements[image, {"BoundingBox", "Rectangularity"}] but the problem is that it fits big rectangles around and not in the comb's teeths. $\endgroup$
    – user9140
    Aug 20, 2013 at 11:30
  • $\begingroup$ Your sample image is smaller than the size you're cropping to. Or maybe you downsized it for the upload (you didn't have to, it will be shown appropriately anyhow). $\endgroup$
    – C. E.
    Aug 21, 2013 at 17:44
  • $\begingroup$ @anon I just copied the link from the original post to imgur, I hope I didn't get it wrong... $\endgroup$
    – cormullion
    Aug 21, 2013 at 17:47
  • $\begingroup$ @cormullion ah, so it wasn't the OP. Any image is better than no image, but what I observed was that the second line of code doesn't make sense for that picture. $\endgroup$
    – C. E.
    Aug 21, 2013 at 17:50

1 Answer 1

4
$\begingroup$

I have a function to measure a distance. Below I give first a description, and then a function code.

Description

The function measureDistance[image] enables one to get the locators placed into any 2 points of an image, and memorizes them in a global variable entitled "poinTs" and the distance between them - in a global variable "diStance"

Parameters:

image is any image. It should have Head=Image, if it is a Graphics object wrap it with Image statement. The code uses specific Image properties during the rescaling.

Controls:

The Checkbox "whiteLocatorRing" defines, if the locators are shown by a single color ring (unchecked), or with two rings, the outer having a color defined by the ColorSlider (see below), the inner one being white. This may be helpful, if working with a too dark image.

size controls the size of the image. The default value is 450. This slider is used to adjust the size to the one to enable the most comfortable work with the image plot.

opacity controls the opacity of the line connecting the locators

thickness controls the thickness of the double ring that forms each locator.

lineThickness controls the thickness of the line connecting the locators

color is the colour slider that controls the colour of the outer ring forming the locator and the line connecting them. The inner locator ring is always white or no white ring at all.

radius controls the radius of the locators.

InputFields: 1) The input fields Subscript[x, 1] and Subscript[x, 2] should be supplied by the reference points x1 and x2. 2) The input fields "Locators coordinates" and "The inter-locator distance" serve to show the output. The corresponding figures may be copied from these input fields. Alternatively one can evaluate the variables poinTs or diStance

Buttons: The button "Memorize the scale" should be pressed after the first two locators are placed on the corresponding reference points. The button "Pick up the coordinates of locators and evaluate the distance between them" should be pressed at the end of the session. Upon its pressing the actual list of points representing those of the curve is assigned to the global variable "poinTs", and its norm to the global variable "diStance".

Operation sequence:

Step 1: Evaluate the function

Step 2: Enter the reference points at the plot x axes into the input fields. Press Enter.

Step3: Alt+Click on the point with x-coordinate x1. This brings up the first locator visible as a circle. Alt+Click (Command + Click for Mac OS X) on that with x2 which gives rise to the second locator. Adjust the locators, if necessary. Press the button "Memorize scale".

Step 4: Move the two already existing locators to the points of the image the distance between which should be measured. Adjust locators, if necessary.

Step 5: Press the button "Pick up the coordinates ...". This assigns the captured coordinates to the global variable "poinTs" and the distance between them to the global variable "diStance". Done.

The results can be read in and copied from the InputFields "Locators coordinates" and "The inter-locator distance", or can be accessed by evaluation the variables "poinTs" and "diStance".

The "poinTs" and "diStance" are a global variable. They can be addressed everywhere in the notebook.

The function measureDistance

Clear[measureDistance];

measureDistance[image_] :=

  Manipulate[
   DynamicModule[{pts = {}, x1 = Null, x2 = Null, \[CapitalDelta]X, X1, X2, g,
      myRound},

    myRound[x_] := Round[1000*x]/1000 // N;

    (* Begins the column with all the content of the manipulate *)
    Column[{
      (* Begin LocatorPane*)
      Dynamic@LocatorPane[Union[Dynamic[pts]],
        Dynamic@
         Show[{Image[image, ImageSize -> size], 
           Graphics[{color, AbsoluteThickness[lineThickness], 
             Opacity[opacity], Line[Union[pts]]}]
               }], LocatorAutoCreate -> True,
        (* Begin Locator appearance *)
        Appearance -> If[whiteLocatorRing,
          Graphics[{{color, AbsoluteThickness[thickness], 
             Circle[{0, 0}, radius + thickness/2]}, {White, 
             AbsoluteThickness[thickness], Circle[{0, 0}, radius]}}, 
           ImageSize -> 10]
          , 
          Graphics[{{color, AbsoluteThickness[thickness], 
             Circle[{0, 0}, radius + thickness/2]}}, ImageSize -> 10]](* 
        End Locator appearance *)
                              ],(* End LocatorPane*)

      (* Begin of the block of InputFields *)
      Row[{ Style["\!\(\*SubscriptBox[\(x\), \(1\)]\):"],
        InputField[Dynamic[x1], 
         FieldHint -> "Type  \!\(\*SubscriptBox[\(x\), \(1\)]\)", 
         FieldSize -> 7, FieldHintStyle -> {Red}],
        Spacer[20], Style["   \!\(\*SubscriptBox[\(x\), \(2\)]\):"],
        InputField[Dynamic[x2], 
         FieldHint -> "Type  \!\(\*SubscriptBox[\(x\), \(2\)]\)", 
         FieldSize -> 7, FieldHintStyle -> {Red}],
        (* Begin button "Memorize scale X" *)
        Spacer[30],
        Button["Memorize scale",
         X1 = Min[Transpose[myRound /@ Union[pts]][[1]]];
         X2 = Max[Transpose[myRound /@ Union[pts]][[1]]];
         \[CapitalDelta]X = X2 - X1;
         ](* End of button "Memorize scale X" *)
              }],
      (* End of the block of InputFields and button *)
      Spacer[20],
      (* Begin button "Make the list of the curve's points" *)
      Button[
       Style["Pick up the locators coordinates and calculate the distance \
between them" , Bold],
       g[{a_, b_}] := {(x1*X2 - x2*X1)/\[CapitalDelta]X + 
          a/\[CapitalDelta]X*Abs[x2 - x1], (x1*X2 - x2*X1)/\[CapitalDelta]X + 
          b/\[CapitalDelta]X*Abs[x2 - x1]};
       Clear[poinTs, diStance];
       poinTs = Map[myRound, Map[g, pts]];
       diStance = poinTs // Differences // Norm
                   ],(* End of button "Make the list..." *)
      Row[{Style["Locators coordinates:      ", 14, Blue, Bold], 
        InputField[Dynamic[poinTs]]}],
      Spacer[10],
      Row[{Style["The inter-locator distance: ", 14, Blue, Bold], 
        InputField[Dynamic[diStance]]}]

      }, Alignment -> Center](* 
    End of column with all the content of the manipulate *)
    ],(* End of the DynamicModule *)

   (* The massive of sliders  begins *)
   Column[{Row[{Control[{whiteLocatorRing, {True, False}}], Spacer[50]}],
     Row[{Spacer[32.35], Control[{{size, 450}, 300, 800}], Spacer[38.5`], 
       Control[{{opacity, 0.5}, 0, 1}]}],
     Row[{Spacer[10.], Control[{{thickness, 1}, 0.5, 5}], Spacer[13.65], 
       Control[{{lineThickness, 1}, 0, 10}] }],
     Row[{Spacer[22.8], Control[{color, Red}], Spacer[59.3], 
       Control[{{radius, 0.5}, 0, 3}]}]
     }, Alignment -> Center],(* The massive of sliders ends *)

   (* Definitions of sliders *)
   ControlType -> {Checkbox, Slider, Slider, Slider, Slider, ColorSlider, 
     Slider},
   ControlPlacement -> Top, SaveDefinitions -> True
   ];

The example of what you should see on the screen is shown in the picture below: enter image description here

$\endgroup$

Your Answer

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

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