Skip to content

sylva.registration

sylva.registration

Rigid registration of scan positions.

kabsch

kabsch(source: ndarray, target: ndarray) -> np.ndarray

Best rigid transform between paired points (Kabsch 1976; Umeyama 1991, no scale).

Use with matched targets (reflectors, tie points) to register scans.

Parameters:

Name Type Description Default
source ndarray

(N, 3) corresponding points, N >= 3 and not collinear.

required
target ndarray

(N, 3) corresponding points, N >= 3 and not collinear.

required

Returns:

Type Description
ndarray

(4, 4) matrix mapping source onto target in the least squares sense.

icp

icp(source: PointCloud, target: PointCloud, init: ndarray | None = None, max_correspondence_distance: float = 0.5, max_iterations: int = 50, tolerance: float = 1e-06, method: str = 'point', trim: float = 1.0, normal_k: int = 12) -> tuple[np.ndarray, dict]

Iterative closest point.

method is "point" (point-to-point, Besl & McKay 1992) or "plane" (point-to-plane, Chen & Medioni 1992, linearised as in Low 2004; normals from target.attrs['nx','ny','nz'] if present, else PCA). trim keeps that fraction of closest correspondences per iteration (trimmed ICP for partial overlap, Chetverikov et al. 2002).

Parameters:

Name Type Description Default
source PointCloud

Cloud to move.

required
target PointCloud

Fixed reference cloud.

required
init ndarray | None

Starting (4, 4) transform (e.g. from the SOP or kabsch); ICP only converges from a start within about max_correspondence_distance.

None
max_correspondence_distance float

Pairs further apart (m) are ignored.

0.5
max_iterations int

Iteration limit.

50
tolerance float

Stop when the RMSE changes less than this.

1e-06
method ('point', 'plane')

Error metric; point-to-plane converges faster on stems and ground.

"point"
trim float

Fraction of pairs kept (0-1).

1.0
normal_k int

Neighbours for PCA normals with "plane".

12

Returns:

Name Type Description
transform ndarray

(4, 4) matrix mapping source onto target.

info dict

rmse (m), iterations and n_correspondences.

Notes

Thin both clouds to 2-5 cm first; ICP on full-density TLS is slow and no more accurate. Check the result with sylva.quality.stem_noise.

estimate_normals

estimate_normals(cloud: PointCloud, k: int = 12) -> np.ndarray

PCA normals; the same as sylva.filters.estimate_normals.

Parameters:

Name Type Description Default
cloud PointCloud

Input points.

required
k int

Neighbours per point.

12

Returns:

Type Description
ndarray

(N, 3) unit normals with arbitrary sign.

merge_scans

merge_scans(clouds: list[PointCloud], transforms: list[ndarray] | None = None, scan_ids: bool = True) -> PointCloud

Put several scans in one frame and merge them.

Parameters:

Name Type Description Default
clouds list[PointCloud]

One cloud per scan position.

required
transforms list[ndarray] | None

One (4, 4) matrix per cloud (SOPs or ICP results); None if the clouds are already registered.

None
scan_ids bool

Add a scan_id attribute (int32, the index in clouds), which sylva.quality.stem_noise can read.

True

Returns:

Type Description
PointCloud

Only attributes present in every cloud are kept.

rotation_z

rotation_z(angle_deg: float) -> np.ndarray

Rotation about the z axis.

Parameters:

Name Type Description Default
angle_deg float

Angle in degrees, counter-clockwise seen from above.

required

Returns:

Type Description
ndarray

(4, 4) matrix.

translation

translation(dx: float, dy: float, dz: float) -> np.ndarray

Translation matrix.

Parameters:

Name Type Description Default
dx float

Shift (m).

required
dy float

Shift (m).

required
dz float

Shift (m).

required

Returns:

Type Description
ndarray

(4, 4) matrix.