11
$\begingroup$

I need your help to detect the radius of the blue circle in images like this:

Mathematica graphics.

Reading these questions: 1, 2, 3, I figured that this code taken from @halirutan answer to question n°3 could help:

imga=Import[http://i.stack.imgur.com/moV5x.png];
circle = SelectComponents[
MorphologicalComponents[
LaplacianGaussianFilter[ColorNegate@imga, 2], 0.003`], 
"Count", # > 400 &];
Colorize[circle]

And I have this output:

Mathematica graphics

And now I'm stuck, if the circle were closed it would be simple, how can I find the best circle completing it? Obviously if you have a completely different solution it is fine (for example I thought it could be possible to take only the left semicircle, but I don't know how to do it).

Please note that I have to process about 100 images like that so if possible I would need the program to run in a reasonable amount of time and with no manual adjustments.

As a bonus question: since I have to determine also a possible error on the radius, would it be possible to find the outer and the inner circles? and the eccentricity of the closed figure (if it is possible to close it)?

P.S.: since I just started working with mathematica for image processing I would also like to know a few good references or books about it. I have read a few mathematica books but none of them treated this argument in detail.

$\endgroup$
  • $\begingroup$ related: Derive a smooth circle with cusp from an image $\endgroup$ – Murta Feb 20 '14 at 11:47
  • $\begingroup$ If you can grab a few points on the partial circle (at least 3 but preferably several more) then you can set up a linear least squares problem to find the center and radius (yes, I mean linear). $\endgroup$ – Daniel Lichtblau Feb 20 '14 at 19:39
  • $\begingroup$ The above method and code is a great solution, but I wanted to point out an alternative concept, that could be used. I'm not a coder, just puting this out there in case others have a desire to get creative. I could see this method being used if the circle pixel density was low or very irregular. The below uses six points and three lines to find the center, but it could be extended to use dozens of pairs of pixels on even a very irregular vague circle to find the center. Likely throwing away outliers or using a regression method to find the highest desity of center lines to find the best center $\endgroup$ – Charles Richards Jan 9 at 6:01
14
$\begingroup$

You're close: You can use ComponentMeasurements to calculate measurements for each component. (The same measurements that you can use in SelectComponents, too). If you can get away with the center of mass of the marked pixels, it'really quite simple:

imga = Import["http://i.stack.imgur.com/moV5x.png"];
binary = MorphologicalComponents[
  LaplacianGaussianFilter[ColorNegate@imga, 2], 0.003`]; 
components = ComponentMeasurements[
  binary, {"Count", "AreaRadiusCoverage", "Centroid", 
   "MeanCentroidDistance", "MaxCentroidDistance", 
   "MinCentroidDistance"}, #1 > 400 && #2 < 0.01 &];

The "AreaRadiusCoverage" measurement helps to select components where most pixels are near the border. This returns a list of rules, one for each component that matches the filter function:

{193 -> {4516, 0., {222.283, 211.7}, 181.482, 200.342, 167.181}}

Where 193 is the component index, 4516 is the pixel count, 0. is AreaRadiusCoverage and so on.

The found circle looks like this:

Show[HighlightImage[imga, Image[1 - Abs[binary - components[[1, 1]]]]],
 Graphics[
  {
   Yellow, Dashed,
   components /. 
     {(_ -> {count_, areaRadiusCoverage_, center_, radii__}) 
     :> (Circle[center, #] & /@ {radii})}}]]

enter image description here


If the center of mass is not good enough, you can use the "Mask" component measurement to get an image of the marked pixels in each component:

imga = Import["http://i.stack.imgur.com/moV5x.png"];
binary = MorphologicalComponents[
  LaplacianGaussianFilter[ColorNegate@imga, 2], 0.003`]; components = 
 ComponentMeasurements[
  binary, {"Count", "AreaRadiusCoverage", 
   "Mask"}, #1 > 400 && #2 < 0.01 &];

The "mask" value returned looks like this:

Image[components[[1, 2, 3]]]

enter image description here

Getting the coordinates of the white pixels in this image is easy:

whitePixels = PixelValuePositions[Image[components[[1, 2, 3]]], 1];

Now, what we're looking for is a circle with center (cx,cy) and radius r that minimized:

err = Total[
  Map[
      Function[whitePixel, (Norm[{cx, cy} - whitePixel] - r)^2], 
      whitePixels]];

i.e. the squared distance of the white pixels from the circle.

We can simply pass this error function to FindMinimum:

optimalCircle = FindMinimum[err, {cx, cy, r}]

To get a better estimate of the circle:

Show[HighlightImage[imga, Image[components[[1, 2, 3]]]],
 Graphics[
  {
   Yellow, Dashed,
   Circle[{cx, cy}, r] /. optimalCircle[[2]]}]]

enter image description here

(Finding the min/max radius should be straightfoward from here - you have the center and the white pixel coordinates.)

$\endgroup$
  • $\begingroup$ Thank you for you answer, works great! I managed to write the code of the first part, but as you said I didn't want to use the centroid because it is imprecise in this situation. Can you explain a little more why you set the "AreaRadiusCoverage" to be <0.01 and what exactly it does? $\endgroup$ – John Feb 20 '14 at 14:31
  • $\begingroup$ @John: I think AreaRadiusCoverage (for each connected component) takes a disc centered at the centroid, with the same area as the component, and measures the overlap between that disc and the component. For a ring, where all pixels are far from the centroid, this should always be small. Other suitable measurements might be the ConvexCoverage, or maybe something like (MaxCentroidDistance-MinCentroidDistance)/MeanCentroidDistance $\endgroup$ – Niki Estner Feb 20 '14 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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