Skip to main content

Ellipse counting from image


I have hundreds of images similar to this image:


Valid XHTML


I'm looking to count the number of ellipses as well as their major and minor diameters. The ellipses are touching, hence,


img = Import["http://i.imgur.com/oNrJq0j.png"]
EdgeDetect[img]

doesn't return fillable ellipses so using something like


WatershedComponents[%]


returns useless information.


I'm very new to this type of image analysis using Mathematica so any help would be greatly appreciated.



Answer



One standard way to detect circular shapes is to binarize the image and apply a distance transform: The maxima locations of the distance transform are the centers of the circles.


To make this work on your ellipses, I first have to stretch them to be (roughly) circular, as @Rahul Narain suggested in a comment:


img = ColorConvert[Import["http://i.imgur.com/oNrJq0j.png"], 
"Grayscale"]
{w, h} = ImageDimensions[img];
ir = ImageResize[img, {w/5, h*5}]

stretched = ImageRotate[ImageResize[img, {w/5, h*5}], 90 \[Degree]]

enter image description here


(The rotation is just for display. If I don't rotate the image by 90°, this post will be very long. It has no influence on the ellipse detection.)


Calculating the distance transform and finding the maxima is easy:


distTransform = DistanceTransform[Binarize[stretched]];    
maxima = MaxDetect[distTransform, 2];

Here's a display of the result so far:


Grid[

Transpose[{
{"Stretched", "Binarized", "Distance Transform",
"Distance Transform Maxima"},
Image[#, ImageSize -> All] & /@ {
stretched,
Binarize[stretched],
ImageAdjust[distTransform],
HighlightImage[stretched, maxima]}
}]]


enter image description here


Now I can use ComponentMeasuements to find connected maxima locations, their orientation, shape and the max. value in the distance transform image:


components = 
ComponentMeasurements[{MorphologicalComponents[maxima],
distTransform}, {"Centroid", "SemiAxes", "Orientation", "Max"}];

Show[stretched, Graphics[
{
Red,
components[[All, 2]] /.

{
{centroid_, semiAxes_, orientation_, maxR_} :>
Rotate[Circle[
centroid, {maxR*Sqrt[semiAxes[[1]]/semiAxes[[2]]], maxR}],
orientation, centroid]
}
}]]

enter image description here


As you can see:




  • the centroids are quite good, except for the ellipses near the border, because the border changes the distance transform

  • the minor radius of the ellipses is ok (it's just the distance from the center to the nearest background point)

  • the estimated orientation is ok,

  • but the semi-axis length ratio of the maximum is only a rough estimate.

  • You could probably apply smoothing before binarizing, use MorphologicalBinarize and hand-tune the filter/binarization parameters to improve the result


ADD:


I can improve the shape estimate a bit further using WatershedComponents, with the distance transform maxima as seed points:


watershedComponents = 

WatershedComponents[ColorNegate[distTransform], maxima] *
ImageData[Binarize[stretched]];
Colorize[watershedComponents]

enter image description here


Estimated Ellipses:


components = 
ComponentMeasurements[
watershedComponents, {"Centroid", "SemiAxes", "Orientation"}];


Show[stretched, Graphics[
{
Red,
components /.
{
(n_ -> {centroid_, semiAxes_, orientation_}) :>
{
Rotate[Circle[centroid, semiAxes], orientation, centroid],
Text[n, centroid]
}

}
}]]

enter image description here


Comments

Popular posts from this blog

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...