Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fitting a circle to a binary image

I have been using skim age's thresholding algorithms to get some binary mask. For example, I obtain binary images like this:

Binary image obtained as a result of Otsu thresholding

What I am trying to figure out is how can I fit a circle to this binary mask. The constraint is the circle should cover as much of the white areas as possible and the whole circumference of the circle should lie entirely on the white parts.

I have been wrecking my head on how I can do this efficiently but have come up with no solution that works.

One approach I thought that might be something is:

  • Find some optimal center of the image/circle (I am not sure how to do this yet. Perhaps some raster scan like approach).
  • Compute circle for increasing radiii and figure out when it starts getting out of the white area or outside the image.
  • Then the centroid and radius will describe the circle.
like image 577
Luca Avatar asked Feb 02 '15 16:02

Luca


3 Answers

Here is a solution that tries to make an optimal circle fit via minimization. It soon becomes apparent that the bubble isn't a circle :) Note the use of "regionprops" for easily determining area, centroid, etc. of regions.

Circle fit to bubble

from skimage import io, color, measure, draw, img_as_bool
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt


image = img_as_bool(io.imread('bubble.jpg')[..., 0])
regions = measure.regionprops(measure.label(image))
bubble = regions[0]

y0, x0 = bubble.centroid
r = bubble.major_axis_length / 2.

def cost(params):
    x0, y0, r = params
    coords = draw.disk((y0, x0), r, shape=image.shape)
    template = np.zeros_like(image)
    template[coords] = 1
    return -np.sum(template == image)

x0, y0, r = optimize.fmin(cost, (x0, y0, r))

import matplotlib.pyplot as plt

f, ax = plt.subplots()
circle = plt.Circle((x0, y0), r)
ax.imshow(image, cmap='gray', interpolation='nearest')
ax.add_artist(circle)
plt.show()
like image 68
Stefan van der Walt Avatar answered Oct 03 '22 22:10

Stefan van der Walt


This should in general give very good and robust results:

import numpy as np
from skimage import measure, feature, io, color, draw
import matplotlib.pyplot as plt

img = color.rgb2gray(io.imread("circle.jpg"))
img = feature.canny(img).astype(np.uint8)
img[img > 0] = 255

coords = np.column_stack(np.nonzero(img))

model, inliers = measure.ransac(coords, measure.CircleModel,
                                min_samples=3, residual_threshold=1,
                                max_trials=500)

print(model.params)

rr, cc = draw.disk((model.params[0], model.params[1]), model.params[2],
                   shape=img.shape)

img = img * 0.5
img[rr, cc] += 128

plt.imshow(img)
plt.show()

circle fit to speech bubble

like image 34
user2970139 Avatar answered Oct 03 '22 23:10

user2970139


This is actually a mostly solved problem in image processing. Looks like what you want is a Hough Transform, specifically the circular or elliptical kind. I believe the circular one is a bit less computationally intensive in general.

Here are some code examples for scikit-image that show pretty much exactly what you're trying to do. And here is a link to the documentation.

like image 39
Mali Akmanalp Avatar answered Oct 04 '22 00:10

Mali Akmanalp