def find_ellipse(image, mode='ellipse_aligned', min_length=24):
""" Find bright ellipse contours on a black background.
This routine thresholds the image (using the Otsu threshold), finds the
longest contour and fits an ellipse to this contour.
Parameters
----------
image : ndarray, 2d
mode : {'ellipse', 'ellipse_aligned', 'circle'}
'ellipse' or None finds an arbitrary ellipse (default)
'circle' finds a circle
'ellipse_aligned' finds an ellipse with its axes aligned along [x y] axes
min_length : number, optional
minimum length of the ellipse contour, in pixels. Default 24.
Returns
-------
yr, xr, yc, xc when dimension order was y, x (most common)
xr, yr, xc, yc when dimension order was x, y
"""
assert image.ndim == 2
# Threshold the image
thresh = threshold_otsu(image)
binary = image > thresh
# Find the contours of 0.5 value. For a thresholded ellipse contour, this
# likely finds 2 contours: the inner and the outer.
contours = find_contours(binary, 0.5, fully_connected='high')
if len(contours) == 0:
raise ValueError('No contours found')
# Eliminate short contours
contours = [c for c in contours if len(c) >= min_length]
# fit circles to the rest, keep the one with lowest residual deviation
result = [np.nan] * 4
residual = None
for c in contours:
try:
(xr, yr), (xc, yc), _ = fit_ellipse(c.T, mode=mode)
if np.any(np.isnan([xr, yr, xc, yc])):
continue
x, y = c.T
r = np.sum((((xc - x)/xr)**2 + ((yc - y)/yr)**2 - 1)**2)/len(c)
if residual is None or r < residual:
result = xr, yr, xc, yc
residual = r
except np.linalg.LinAlgError:
pass
return result
评论列表
文章目录