pyRANSAC-3D

repository·master·Indexed 20 days ago

https://github.com/leomariga/pyransac-3d

A Python tool for fitting primitive 3D shapes to point clouds using the Random Sample Consensus (RANSAC) algorithm. Version 0.6.1 supports fitting planes, cylinders, cuboids, spheres, lines, circles, and points. It provides specialized classes for each shape, including methods to define distance thresholds and maximum iterations, as well as callback functionality for monitoring progress or implementing early stopping.

Tokens
6.7K
Snippets
18
Records
20
Agent score
66%

What's inside pyransac3d

  1. Customize fitting with the Point.fit callback

    master

    The fit method in the Point class accepts an optional callback function. This is useful for monitoring progress, visualizing intermediate results, or implementing custom early-stopping logic.

    The callback is called with a dict containing the following read-only keys:

    • iteration: current iteration index (0-based)
    • sample_indices: indices of the points sampled this iteration
    • sample_points: the sampled points, np.array (1, 3)
    • model: dict with this iteration's candidate center
    • inliers: inlier indices found for this iteration's candidate
    • best_model: dict with the best center found so far
    • best_inliers: best inlier indices found so far
    • is_best: True if this iteration became the new best candidate

    To stop the fitting process early, ensure the callback returns a truthy value.

    def my_callback(state):
        print(f"Iteration {state['iteration']}: Best inliers found: {len(state['best_inliers'])}")
        # Stop if we found more than 50 inliers
        if len(state['best_inliers']) > 50:
            return True
        return False
    
    # Usage
    ransac.fit(pts, callback=my_callback)
  2. Perform Spherical RANSAC to fit a sphere

    master

    To fit a sphere to a point cloud, use the Sphere class from pyransac3d.

    1. Load your point cloud as a NumPy array with shape (N, 3).
    2. Instantiate pyrsc.Sphere().
    3. Call .fit(points, thresh=threshold).

    The method returns the center coordinates, the radius, and the indices of the inlier points.

    import pyransac3d as pyrsc
    
    # points must be a numpy array (N, 3)
    points = load_points(.) 
    
    sph = pyrsc.Sphere()
    # center: [x, y, z], radius: float, inliers: array of inlier indices
    center, radius, inliers = sph.fit(points, thresh=0.4)
  3. Perform Planar RANSAC to fit a plane

    master

    To fit a plane to a point cloud, use the Plane class from pyransac3d.

    1. Load your point cloud as a NumPy array with shape (N, 3).
    2. Instantiate pyrsc.Plane().
    3. Call .fit(points, threshold) where threshold is the distance tolerance.

    The method returns the plane equation in the form Ax + By + Cz + D and the indices of the inlier points.

    import pyransac3d as pyrsc
    
    # points must be a numpy array (N, 3)
    points = load_points(.) 
    
    plane1 = pyrsc.Plane()
    # best_eq is [A, B, C, D], best_inliers is the array of inlier indices
    best_eq, best_inliers = plane1.fit(points, 0.01)
  4. Use a callback with Cylinder.fit for monitoring or early stopping

    master

    You can provide a callback function to Cylinder.fit to inspect the progress of the RANSAC algorithm. This is useful for plotting progress or implementing custom early-stopping criteria.

    The callback receives a dict containing the following state keys (all arrays are read-only):

    • iteration: Current iteration index (0-based).
    • sample_indices: Indices of the points sampled in the current iteration.
    • sample_points: The sampled points (np.array of shape (3, 3)).
    • model: A dict with the current candidate: {'center': ..., 'axis': ..., 'radius': ...}.
    • inliers: Inlier indices found for the current candidate.
    • best_model: A dict with the best candidate found so far: {'center': ..., 'axis': ..., 'radius': ...}.
    • best_inliers: The best inlier indices found so far.
    • is_best: True if the current iteration produced a new best candidate.

    If the callback returns a truthy value, the fitting process terminates immediately.

    def my_callback(state):
        print(f"Iteration {state['iteration']}: Best inliers so far = {len(state['best_inliers'])}")
        # Example early stopping: stop if we find more than 50 inliers
        if len(state['best_inliers']) > 50:
            return True
        return False
    
    # Pass the callback to the fit method
    center, axis, radius, inliers = cylinder.fit(pts, callback=my_callback)
  5. Fit a 3D line using the Line class

    master

    The Line class implements the RANSAC (Random Sample Consensus) method to find the best equation for a line in 3D space. It selects two random points from a point cloud to define a candidate line and iteratively improves the model by maximizing the number of inliers within a specified distance threshold.

    To use it, instantiate Line() and call the .fit() method with your point cloud.

    Mathematical Representation: The resulting line is defined by the equation $y = Ax + B$, where:

    • A is the 3D slope (direction vector) as a np.array (1, 3).
    • B is the axis interception as a np.array (1, 3).

    Parameters:

    • pts: A 3D point cloud as a np.array (N, 3).
    • thresh: The distance threshold. Points within this distance from the line are considered inliers (default: 0.2).
    • maxIteration: The maximum number of RANSAC iterations (default: 1000).
    • callback: An optional callable for monitoring progress or implementing early stopping.
    import numpy as np
    from pyransac3d.line import Line
    
    # Create a sample point cloud
    pts = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [5, 0, 0]], dtype=float)
    
    # Initialize and fit the line
    line_fitter = Line()
    A, B, inliers = line_fitter.fit(pts, thresh=0.1, maxIteration=500)
    
    print("Slope A:", A)
    print("Intercept B:", B)
    print("Inlier indices:", inliers)
  6. Use pyRANSAC-3D geometric primitives

    master

    The pyransac3d package provides several geometric primitive classes used for RANSAC-based shape fitting. You can import these directly from the top-level package to represent or fit shapes such as Planes, Spheres, Cylinders, Cuboids, Circles, Lines, and Points.

    from pyransac3d import Plane, Sphere, Cylinder, Cuboid, Circle, Line, Point
  7. Fit a cylinder using the Cylinder class

    master

    The Cylinder class implements a RANSAC algorithm to find an infinite height cylinder within a 3D point cloud. It returns the cylinder's axis, center, radius, and the indices of the inlier points.

    Warning: The current implementation of the cylinder RANSAC does not produce good results on real-world data. The developers are working on an improved version using normals.

    To use it, instantiate the class and call the fit method with your point cloud and desired parameters.

    from pyransac3d import Cylinder
    import numpy as np
    
    # Initialize the cylinder model
    cylinder = Cylinder()
    
    # Prepare your 3D point cloud (N, 3) numpy array
    pts = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1]])
    
    # Fit the cylinder
    center, axis, radius, inliers = cylinder.fit(pts, thresh=0.2, maxIteration=1000)
    
    print(f"Center: {center}")
    print(f"Axis: {axis}")
    print(f"Radius: {radius}")
    print(f"Inlier indices: {inliers}")
  8. Use a callback to monitor or stop Line.fit()

    master

    The fit method of the Line class accepts an optional callback function. This function is invoked after every iteration and receives a dict containing the current state. If the callback returns a truthy value, the fitting process stops immediately and returns the current best result.

    State dictionary keys available in the callback:

    • iteration: current iteration index (0-based)
    • sample_indices: indices of the points sampled this iteration
    • sample_points: the sampled points, np.array (2, 3)
    • model: dict with this iteration's candidate A and B
    • inliers: inlier indices found for this iteration's candidate
    • best_model: dict with the best A and B found so far
    • best_inliers: best inlier indices found so far
    • is_best: True if this iteration became the new best candidate

    Note: The arrays in the state dictionary should be treated as read-only.

    This is useful for plotting progress, inspecting intermediate results, or implementing custom early-stopping criteria (e.g., stopping if a certain number of inliers is reached).

    def my_callback(state):
        print(f"Iteration: {state['iteration']} | Inliers: {len(state['inliers'])}")
        # Stop if we find more than 100 inliers
        if len(state['best_inliers']) > 100:
            return True
        return False
    
    line_fitter.fit(pts, callback=my_callback)
  9. Fit a sphere using the Sphere class

    master

    The Sphere class implements the RANSAC algorithm to find the center and radius of a sphere within a 3D point cloud.

    To use it, instantiate the class and call the fit method with your point cloud data. The method requires at least 4 points in the input array.

    Parameters:

    • pts: A numpy array of shape (N, 3) representing the 3D point cloud.
    • thresh: (Optional) The distance threshold from the sphere's surface. Points within this distance of the hull are considered inliers. Default is 0.2.
    • maxIteration: (Optional) The maximum number of RANSAC iterations. Default is 1000.
    • callback: (Optional) A callable invoked after every iteration. If the callback returns a truthy value, the fitting process stops early.

    Returns:

    • center: The center of the sphere as a numpy.array of shape (3,).
    • radius: The radius of the sphere.
    • inliers: An array of indices corresponding to the inlier points from the original point cloud.
    from pyransac3d import Sphere
    import numpy as np
    
    # Create a dummy point cloud
    pts = np.random.rand(100, 3)
    
    # Initialize and fit
    sphere = Sphere()
    center, radius, inliers = sphere.fit(pts, thresh=0.1, maxIteration=500)
    
    print(f"Center: {center}")
    print(f"Radius: {radius}")
    print(f"Inlier count: {len(inliers)}")
  10. Use a callback to monitor Sphere RANSAC progress

    master

    The Sphere.fit method accepts an optional callback function. This is useful for real-time visualization, inspecting intermediate results, or implementing custom early-stopping logic.

    The callback receives a dict containing the current state. Note that the arrays in this dictionary should be treated as read-only.

    State Dictionary Keys:

    • iteration: Current iteration index (0-based).
    • sample_indices: Indices of the 4 points sampled this iteration.
    • sample_points: The 4 sampled points (np.array of shape (4, 3)).
    • model: Dictionary containing the current candidate center and radius.
    • inliers: Indices of inliers found for the current candidate.
    • best_model: Dictionary containing the best center and radius found so far.
    • best_inliers: Indices of the best inliers found so far.
    • is_best: Boolean, True if the current iteration produced a new best model.

    To stop the fitting process early, ensure your callback returns a truthy value.

    def my_callback(state):
        print(f"Iteration: {state['iteration']} | Best Inliers: {len(state['best_inliers'])}")
        # Stop if we find more than 50 inliers
        if len(state['best_inliers']) > 50:
            return True
        return False
    
    sphere = Sphere()
    center, radius, inliers = sphere.fit(pts, callback=my_callback)