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

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],