A comprehensive collection of circle fitting algorithms for Python that enable finding optimal circle parameters from 2D point data.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
A comprehensive collection of circle fitting algorithms for Python that enable finding optimal circle parameters from 2D point data. Circle-fit implements multiple sophisticated algorithms including hyperLSQ for high-accuracy least squares fitting, standardLSQ for basic least squares, various SVD-based algebraic approaches (Pratt, Taubin, hyperSVD), geometric methods (Riemann SWFL), iterative optimization (Levenberg-Marquardt), and consistent fitting (KMH method). All algorithms provide a uniform API and support both Python lists and NumPy arrays as input.
pip install circle-fitfrom circle_fit import taubinSVD, hyperLSQ, standardLSQ, plot_data_circle
from typing import Union, List, Tuple
import numpy as np
import numpy.typing as nptImport all available algorithms:
from circle_fit import (
riemannSWFLa, lm, prattSVD, taubinSVD, hyperSVD,
kmh, hyperLSQ, standardLSQ, plot_data_circle
)
from typing import Union, List, Tuple
import numpy as np
import numpy.typing as nptLegacy aliases:
from circle_fit import hyper_fit, least_squares_circlefrom circle_fit import taubinSVD, plot_data_circle
import numpy as np
# Create sample 2D points (as Python list)
point_coordinates = [[1, 0], [-1, 0], [0, 1], [0, -1]]
# Fit a circle using Taubin SVD algorithm (recommended default)
xc, yc, r, sigma = taubinSVD(point_coordinates)
print(f"Center: ({xc:.3f}, {yc:.3f}), Radius: {r:.3f}, Error: {sigma:.6f}")
# Also works with NumPy arrays
points_array = np.array([[1, 0], [-1, 0], [0, 1], [0, -1]])
xc, yc, r, sigma = taubinSVD(points_array)
# Visualize the fit (requires matplotlib)
plot_data_circle(point_coordinates, xc, yc, r)For most use cases, taubinSVD() provides a good balance of accuracy and robustness.
def taubinSVD(coords: Union[npt.NDArray, List]) -> Tuple[float, ...]:
"""
Algebraic circle fit by G. Taubin using SVD.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""For maximum accuracy when precision is critical.
def hyperLSQ(coords: Union[npt.NDArray, List], iter_max: int = 99) -> Tuple[float, ...]:
"""
Hyper least squares fitting with "hyperaccuracy" by Kenichi Kanatani, Prasanna Rangarajan.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
- iter_max (int, optional): Maximum number of iterations. Defaults to 99.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""Basic least squares circle fitting for simple cases.
def standardLSQ(coords: Union[np.ndarray, List]) -> Tuple[float, ...]:
"""
Standard least squares circle fitting.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""Levenberg-Marquardt algorithm minimizing orthogonal distances in full parameter space.
def lm(coords: Union[npt.NDArray, List], par_ini: npt.NDArray, iter_max: int = 50, lambda_ini: float = 1, epsilon: float = 0.00001) -> Tuple[float, ...]:
"""
Geometric circle fit using Levenberg-Marquardt scheme.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
- par_ini: 1D np.ndarray. Array of [xc, yc, r] initial guess.
- iter_max (int, optional): Maximum iterations. Defaults to 50.
- lambda_ini (float, optional): Initial correction factor. Defaults to 1.
- epsilon (float, optional): Convergence threshold. Defaults to 0.00001.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""Fast algebraic approaches using Singular Value Decomposition.
def prattSVD(coords: Union[npt.NDArray, List]) -> Tuple[float, ...]:
"""
Algebraic circle fit by V. Pratt using SVD.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""
def hyperSVD(coords: Union[npt.NDArray, List]) -> Tuple[float, ...]:
"""
Algebraic circle fit with "hyperaccuracy" using SVD.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""Advanced algorithms for specific use cases.
def riemannSWFLa(coords: Union[npt.NDArray, List]) -> Tuple[float, ...]:
"""
Riemann circle fit, SWFL version A by Strandlie, Wroldsen, Fruhwirth, and Lillekjendlie.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""
def kmh(coords: Union[npt.NDArray, List], iter_max: int = 99, epsilon: float = 1E-9) -> Tuple[float, ...]:
"""
Consistent circle fit by A. Kukush, I. Markovsky, S. Van Huffel.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
- iter_max (int, optional): Maximum iterations. Defaults to 99.
- epsilon (float, optional): Convergence threshold. Defaults to 1E-9.
Returns:
tuple: (xc, yc, r, sigma) where:
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
- sigma (float): RMS error of the fit
"""Plot data points with fitted circle overlay.
def plot_data_circle(coords: Union[npt.NDArray, List], xc: float, yc: float, r: float) -> None:
"""
Plot data points with fitted circle overlay using matplotlib.
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
- xc (float): x coordinate of circle center
- yc (float): y coordinate of circle center
- r (float): radius of fitted circle
Returns:
None
Raises:
ModuleNotFoundError: If matplotlib is not installed.
"""Deprecated functions provided for backwards compatibility.
def hyper_fit(coords: Union[npt.NDArray, List], IterMax: int = 99) -> Tuple[float, ...]:
"""
Deprecated alias for hyperLSQ().
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
- IterMax (int, optional): Maximum iterations. Defaults to 99.
Returns:
tuple: (xc, yc, r, sigma)
Note:
This function is deprecated. Use hyperLSQ() instead.
"""
def least_squares_circle(coords: Union[npt.NDArray, List]) -> Tuple[float, ...]:
"""
Deprecated alias for standardLSQ().
Parameters:
- coords: 2D List or 2D np.ndarray of shape (n,2). X,Y point coordinates.
Returns:
tuple: (xc, yc, r, sigma)
Note:
This function is deprecated. Use standardLSQ() instead.
"""# Input coordinate formats
CoordinateInput = Union[List[List[float]], npt.NDArray]
# Return type for all fitting functions
CircleFitResult = Tuple[float, float, float, float]
# (xc: center x, yc: center y, r: radius, sigma: RMS error)
# Initial parameter guess for lm() function
InitialParameters = npt.NDArray # Shape: (3,) containing [xc, yc, r]Common exceptions that may be raised:
convert_input() for unsupported coordinate input typesplot_data_circle() when matplotlib is not installedconvert_input() for incorrectly shaped numpy arraysfrom circle_fit import taubinSVD, hyperLSQ, standardLSQ
import numpy as np
# Sample noisy circle data
np.random.seed(42)
theta = np.linspace(0, 2*np.pi, 20)
true_center = (5, 3)
true_radius = 10
noise_level = 0.5
x = true_center[0] + true_radius * np.cos(theta) + np.random.normal(0, noise_level, 20)
y = true_center[1] + true_radius * np.sin(theta) + np.random.normal(0, noise_level, 20)
coords = np.column_stack([x, y])
# Compare different algorithms
algorithms = [
("Taubin SVD", taubinSVD),
("Hyper LSQ", hyperLSQ),
("Standard LSQ", standardLSQ)
]
for name, algorithm in algorithms:
xc, yc, r, sigma = algorithm(coords)
print(f"{name}: Center=({xc:.2f}, {yc:.2f}), Radius={r:.2f}, Error={sigma:.4f}")from circle_fit import lm, taubinSVD
import numpy as np
# First get a rough estimate using Taubin SVD
coords = [[1, 0], [-1, 0], [0, 1], [0, -1], [0.5, 0.8]]
xc_init, yc_init, r_init, _ = taubinSVD(coords)
# Use as initial guess for high-precision LM algorithm
par_ini = np.array([xc_init, yc_init, r_init])
xc, yc, r, sigma = lm(coords, par_ini)
print(f"LM result: Center=({xc:.6f}, {yc:.6f}), Radius={r:.6f}, Error={sigma:.8f}")