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