pyoints.registration package¶
Submodules¶
pyoints.registration.icp module¶
Implementation of the Iterative Closest Point Algorithm.
-
class
pyoints.registration.icp.
ICP
(radii, max_iter=50, max_change_ratio=0.001, assign_class=<class 'pyoints.assign.KnnMatcher'>, update_normals=False, **assign_parameters)[source]¶ Bases:
object
Implementation of a variant of the Iterative Closest Point algorithm [1] with support of multiple point sets and point normals.
Parameters: - radii (array_like(Number, shape=(s))) – Maximum distances in each coordinate dimension to assign corresponding points of k dimensions. The length of radii is equal to 2 * k. If point normals shall also be used to find point pairs, the length of radii is k.
- assign_class (optional, callable class) – Class which assigns pairs of points.
- max_iter (optional, positive int) – Maximum number of iterations.
- update_normals (bool) – Indicates whether or not to also transform the normals.
- **assign_parameters – Parameters passed to assign_class.
Notes
A modified variant of the originally ICP algorithm presented by Besl and McKay (1992) [1]. Inspired by the Normal ICP algorithm of Serafin and Grisetti [2][3] a ICP variant the surface normals has been implemented.
References
[1] P.J. Besl and N.D. McKay (1992): “A Method for Registration of 3-D Shapes”, IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 14 (2): 239-256. [2] J. Serafin and G. Grisetti (2014): “Using augmented measurements to improve the convergence of icp”, International Conference on Simulation, Modeling, and Programming for Autonomous Robots. Springer, Cham: 566-577. [3] J. Serafin and G. Grisetti (2014): “NICP: Dense normal based point cloud registration”, International Conference on Intelligent Robots and Systems (IROS): 742-749.
Examples
Create corresponding point sets.
>>> A = np.array([ ... (0.5, 0.5), (0, 0), (0, -0.1), (1.3, 1), (1, 0), (-1, -2) ... ]) >>> B = np.array([(0.4, 0.5), (0.3, 0), (1, 1), (2, 1), (-1, -2)])
Standard ICP.
>>> coords_dict = {'A': A, 'B': B} >>> radii = (0.25, 0.25) >>> weights = {'A': [1, 1, 1]} >>> icp = ICP(radii, max_iter=10, k=1) >>> T, pairs, report = icp(coords_dict, weights=weights)
>>> tA = T['A'].to_local(A) >>> tB = T['B'].to_local(B)
>>> print_rounded(tA, 1) [[ 0.5 0.5] [ 0. 0. ] [ 0. -0.1] [ 1.3 1. ] [ 1. 0. ] [-1. -2. ]] >>> print_rounded(tB, 1) [[ 0.6 0.5] [ 0.4 0. ] [ 1.2 0.9] [ 2.2 0.9] [-1. -1.9]]
Find matches and compare RMSE (Root Mean Squared Error).
>>> matcher = assign.KnnMatcher(tA, radii) >>> pairs = matcher(tB)
>>> rmse = distance.rmse(A[pairs[:, 0], :], B[pairs[:, 1], :]) >>> print_rounded(rmse, 2) 0.18
>>> rmse = distance.rmse(tA[pairs[:, 0], :], tB[pairs[:, 1], :]) >>> print_rounded(rmse, 2) 0.09
ICP also takes advantage of normals (NICP).
>>> from pyoints.normals import fit_normals >>> normals_r = 1.5 >>> normals_dict = { ... 'A': fit_normals(A, normals_r), ... 'B': fit_normals(B, normals_r) ... } >>> radii = (0.25, 0.25, 0.3, 0.3)
>>> nicp = ICP(radii, max_iter=10, k=1) >>> T, pairs, report = nicp(coords_dict, normals_dict=normals_dict)
>>> tA = T['A'].to_local(A) >>> print_rounded(tA) [[ 0.5 0.5] [ 0. 0. ] [ 0. -0.1] [ 1.3 1. ] [ 1. 0. ] [-1. -2. ]]
>>> tB = T['B'].to_local(B) >>> print_rounded(tB) [[ 0.4 0.5] [ 0.3 0. ] [ 1. 1. ] [ 2. 1. ] [-1. -2. ]]
pyoints.registration.registration module¶
Registration or alignment of point sets.
-
pyoints.registration.registration.
find_rototranslation
(A, B)[source]¶ Finds the optimal roto-translation matrix M between two point sets. Each point of point set A is associated with exactly one point in point set B.
Parameters: A,B (array_like(Number, shape=(n, k))) – Arrays representing n corresponding points with k dimensions. Returns: M – Roto-translation matrix to map B to A with A = B * M.T. Return type: numpy.matrix(float, shape=(k+1, k+1)) Notes
Implements the registration algorithm of Besl and McKay (1992) [1]. The idea has been taken from Nghia Ho (2013) [2]. Code of [2] has been generalized to k dimensional space.
References
[1] P. J. Besl and N. D. McKay (1992): “A Method for Registration of 3-D Shapes”, IEEE Transactions on Pattern Analysis and Machine Intelligence, Institute of Electrical and Electronics Engineers (IEEE), vol. 14, pp. 239-256.
[2] Nghia Ho (2013): “Finding optimal rotation and translation between corresponding 3D points”, URL http://nghiaho.com/?page_id=671.
[3] Nghia Ho (2013): “Finding optimal rotation and translation between corresponding 3D points”, URL http://nghiaho.com/uploads/code/rigid_transform_3D.py_.
Examples
Creates similar, but shifted and rotated point sets.
>>> A = np.array([[0, 0], [0, 1], [1, 1], [1, 0]]) >>> B = transformation.transform(A, transformation.matrix(t=[3, 5], r=0.3))
Finds roto-translation.
>>> M = find_rototranslation(A, B)
>>> C = transformation.transform(B, M, inverse=False) >>> print_rounded(C, 2) [[ 0. 0.] [ 0. 1.] [ 1. 1.] [ 1. 0.]]
-
pyoints.registration.registration.
find_transformation
(A, B)[source]¶ Finds the optimal (non-rigid) transformation matrix M between two point sets. Each point of point set A is associated with exactly one point in point set B.
Parameters: - A (array_like(Number, shape=(n, k))) – Array representing n reference points with k dimensions.
- B (array_like(Number, shape=(n, k))) – Array representing n points with k dimensions.
Returns: M – Tranformation matrix which maps B to A with A = B * M.T.
Return type: np.matrix(Number, shape=(k+1, k+1))
See also
pyoints.registration.rototranslations module¶
Finds roto-translation matrices of multiple point sets.
-
pyoints.registration.rototranslations.
find_rototranslations
(coords_dict, pairs_dict, weights=None)[source]¶ Finds the optimal roto-translation matrices between multiple point sets using pairs of points. The algorithm assumes infinitesimal rotations between the point sets.
Parameters: - coords_dict (dict of array_like(int, shape=(n, k))) – Dictionary of point sets with k dimensions.
- pairs_dict (dict of array_like(int, shape=(m, 2))) – Dictionary of point pairs.
- weights (optional, dict or list or int.) – Tries to keep the original location and orientation by weighting. Each point set can be weighted by a list of values. The first k values represent the weighting factors for location. The last values represent the weighting factors for orientation (angles). The weights can be provided for each point set individially in form of a dictionary. If not provided, weights are set to zero.
Returns: T_dict – Dictionary of desired roto-translation matrices.
Return type: dict of np.ndarray(Number, shape=(k+1, k+1))
Notes
Algorithm idea taken from [1].
References
[1] University of Genoa (2017): URL http://geomatica.como.polimi.it/corsi/def_monitoring/roto-translationsb.pdf.
Examples
2D coordinates.
>>> coordsA = [(-1, -2), (-1, 2), (1, 2), (1, -2), (5, 10)] >>> T = transformation.matrix( ... t=[10, 4], ... r=0.03, ... ) >>> coordsB = transformation.transform(coordsA, T)
>>> coords_dict = {'A': coordsA, 'B': coordsB} >>> pairs_dict = { 'A': { 'B': [(0, 0), (1, 1), (2, 2)] } } >>> weights = {'A': [1, 1, 1], 'B': [0, 0, 0]} >>> res = find_rototranslations(coords_dict, pairs_dict, weights=weights)
>>> print(sorted(res.keys())) ['A', 'B'] >>> tA = res['A'].to_local(coords_dict['A']) >>> print_rounded(tA) [[ -1. -2.] [ -1. 2.] [ 1. 2.] [ 1. -2.] [ 5. 10.]] >>> tB = res['B'].to_local(coords_dict['B']) >>> print_rounded(tB) [[ -1. -2.] [ -1. 2.] [ 1. 2.] [ 1. -2.] [ 5. 10.]]
3D coordinates.
>>> coordsA = [(-10, -20, 3), (-1, 2, 4), (1, 10, 5), (1, -2, 60)]
>>> TB = transformation.matrix( ... t=[2, 5, 10], r=[-0.01, 0.02, 0.03], order='trs') >>> coordsB = transformation.transform(coordsA, TB)
>>> TC= transformation.matrix( ... t=[-2, 3, -6], r=[-0.03, 0.01, -0.02], order='trs') >>> coordsC = transformation.transform(coordsA, TC)
>>> coords_dict = {'A': coordsA, 'B': coordsB, 'C': coordsC} >>> pairs_dict = { ... 'A': { 'B': [(0, 0), (1, 1), (3, 3)], 'C': [(0, 0), (3, 3)] }, ... 'B': { 'A': [(0, 0), (1, 1), (3, 3)], 'C': [(1, 1), (2, 2)] }, ... 'C': { 'A': [(0, 0), (1, 1), (3, 3)], 'B': [(1, 1), (2, 2)] }, ... } >>> weights = {'A': [1, 1, 1, 1, 1, 1], 'B': [0, 0, 0, 0, 0, 0]} >>> res = find_rototranslations(coords_dict, pairs_dict, weights=weights)
>>> print(sorted(res.keys())) ['A', 'B', 'C'] >>> tA = res['A'].to_local(coords_dict['A']) >>> print_rounded(tA, 1) [[-10. -20. 3.] [ -1. 2. 4.] [ 1. 10. 5.] [ 1. -2. 60.]] >>> tB = res['B'].to_local(coords_dict['B']) >>> print_rounded(tB, 1) [[-10. -20. 3.] [ -1. 2. 4.] [ 1. 10. 5.] [ 1. -2. 60.]] >>> tC = res['C'].to_local(coords_dict['C']) >>> print_rounded(tC, 1) [[-10. -20. 3.] [ -1. 2. 4.] [ 1. 10. 5.] [ 1. -2. 60.]]
Module contents¶
Alignment of point clouds.