Source code for pointtree.operations._fit_ellipse

"""Ellipse fitting."""

__all__ = ["fit_ellipse"]

from pointtree.type_aliases import FloatArray, LongArray
from pointtree._operations_cpp import fit_ellipse as fit_ellipse_cpp  # type: ignore[import-untyped] # pylint: disable=import-error, no-name-in-module


[docs] def fit_ellipse(xy: FloatArray, batch_lengths: LongArray, num_workers: int = 1) -> FloatArray: r""" Fits an ellipse to a set of 2D points using the least-squares method described in `Halir, Radim, and Jan Flusser. \ "Numerically Stable Direct Least Squares Fitting of Ellipses." Proc. 6th International Conference in Central \ Europe on Computer Graphics and Visualization. WSCG. Vol. 98. Plzen-Bory: Citeseer, 1998. \ <https://autotrace.sourceforge.net/WSCG98.pdf>`_ This method supports batch processing, i.e., ellipses can be fitted to separate sets of points (batch items) in parallel. For this purpose, :code:`batch_lengths` must be set to specify which point belongs to which set. Args: xy: X- and y- coordinates of the points to which the ellipses are to be fitted. If the :code:`xy` array has a row-major storage layout (`numpy's <https://numpy.org/doc/stable/dev/internals.html>`__ default), a copy of the array is created. To pass :code:`xy` by reference, :code:`xy` must be in column-major format. batch_lengths: Number of points in each item of the input batch. For batch processing, it is expected that all points belonging to the same batch item are stored consecutively in the :code:`xy` input array. For example, if a batch comprises two batch items with :math:`N_1` points and :math:`N_2` points, then :code:`batch_lengths` should be set to :code:`[N_1, N_2]` and :code:`xy[:N_1]` should contain the points of the first batch item and :code:`xy[N_1:]` the points of the second batch item. If :code:`batch_lengths` is set to :code:`None`, it is assumed that the input points belong to a single batch item and batch processing is disabled. num_workers: Number of workers threads to use for parallel processing. If set to -1, all CPU threads are used. Returns: :Parameters of the fitted ellipses in the following order: X- and y-coordinates of the center, radius along the semi-major and along the semi-minor axis, and the counterclockwise angle of rotation from the x-axis to the semi-major axis of the ellipse. If no ellipse is detected for a batch item, all ellipse parameters for this batch item are set to -1. Raises: TypeError: If :code:`xy` or :code:`batch_length` have an invalid shape or data type. ValueError: If the length of :code:`xy` is not equal to the sum of :code:`batch_lengths`. Shape: - :code:`xy`: :math:`(N, 2)` - :code:`batch_lengths`: :math:`(B)` - Output: :math:`(B, 5)` | where | | :math:`B = \text{ batch size}` | :math:`N = \text{ number of points}` """ if not xy.flags.f_contiguous: xy = xy.copy(order="F") # ensure that the input array is in column-major format return fit_ellipse_cpp(xy, batch_lengths, num_workers)