cedalion.geometry.registration

Registrating optodes to scalp surfaces.

Functions

find_spread_points(points_xr)

Selects three points that are spread apart from each other in the dataset.

gen_xform_from_pts(p1, p2)

Calculate the affine transformation matrix T that transforms p1 to p2.

icp_with_full_transform(opt_centers, ...[, ...])

Perform Iterative Closest Point algorithm with full transformation capabilities.

register_general_affine(coords_target, ...)

Finds affine transformation between coords_target and coords_trafo.

register_icp(surface, landmarks, geo3d[, ...])

Iterative Closest Point algorithm for registration.

register_identity(coords_target, coords_trafo)

Affine transformation that just adapts units.

register_optodes_spring_icp(scalp, geo3d, ...)

Register optode coordinates onto a scalp mesh via spring relaxation and ICP.

register_trans_rot(coords_target, coords_trafo)

Finds affine transformation between coords_target and coords_trafo.

register_trans_rot_isoscale(coords_target, ...)

Finds affine transformation between coords_target and coords_trafo.

register_trans_rot_scale(coords_target, ...)

Finds affine transformation between coords_target and coords_trafo.

simple_scalp_projection(geo3d)

Projects 3D coordinates onto a 2D plane using a simple scalp projection.

Classes

SpringICPResult(initial_positions, ...)

Quality-control details returned by register_optodes_spring_icp().

class cedalion.geometry.registration.SpringICPResult(
initial_positions: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
nominal_distances: dict,
spring_errors: DataArray,
landmark_errors: DataArray,
snap_displacement_per_iter: ndarray,
n_iterations: int,
converged: bool,
)[source]

Bases: object

Quality-control details returned by register_optodes_spring_icp().

All distances and displacements are in the same units as the scalp surface.

initial_positions: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')][source]

Positions after the initial landmark alignment, before spring relaxation.

nominal_distances: dict[source]

Nominal channel distances {(src_label, det_label): float} used as spring rest lengths. Measured in scalp units from the phase-1 aligned positions unless supplied by the caller.

spring_errors: DataArray[source]

Per-channel actual_distance - nominal_distance at convergence. Dim channel; auxiliary coords source and detector carry the individual optode labels.

landmark_errors: DataArray[source]

Per-anchor Euclidean distance between the final optode position and the target landmark position at convergence. Dim label. Empty when no optode labels overlap with landmarks_scalp.

snap_displacement_per_iter: ndarray[source]

Maximum surface-projection displacement (over all optodes) per iteration. Shape (n_iterations,). Use as a convergence diagnostic.

n_iterations: int[source]

Number of iterations actually performed.

converged: bool[source]

True when the convergence criterion was met before n_iter.

cedalion.geometry.registration.register_identity(
coords_target: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
coords_trafo: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
)[source]

Affine transformation that just adapts units.

Parameters:
  • coords_target (LabeledPoints) – Target point cloud.

  • coords_trafo (LabeledPoints) – Source point cloud.

Returns:

Affine transformation between the two point clouds.

Return type:

cdt.AffineTransform

cedalion.geometry.registration.register_trans_rot(
coords_target: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
coords_trafo: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
)[source]

Finds affine transformation between coords_target and coords_trafo.

Uses translation and roatation rotation. Requires at least 3 common labels between the two point clouds.

Parameters:
  • coords_target (LabeledPoints) – Target point cloud.

  • coords_trafo (LabeledPoints) – Source point cloud.

Returns:

Affine transformation between the two point clouds.

Return type:

cdt.AffineTransform

cedalion.geometry.registration.register_general_affine(
coords_target: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
coords_trafo: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
)[source]

Finds affine transformation between coords_target and coords_trafo.

This method fits all 12 parameters of a 3D affine transformation without constraints. The transform thus contains scaling, rotation, translation, shear and mirroring, i.e. it can transform between left and right handed coordinate systems.

Parameters:
  • coords_target (LabeledPoints) – Target point cloud.

  • coords_trafo (LabeledPoints) – Source point cloud.

Returns:

Affine transformation between the two point clouds.

Return type:

cdt.AffineTransform

cedalion.geometry.registration.register_trans_rot_isoscale(
coords_target: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
coords_trafo: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
)[source]

Finds affine transformation between coords_target and coords_trafo.

Uses translation, rotation and isotropic scaling. Requires at least 3 common labels between the two point clouds.

Parameters:
  • coords_target (LabeledPoints) – Target point cloud.

  • coords_trafo (LabeledPoints) – Source point cloud.

Returns:

Affine transformation between the two point clouds.

Return type:

cdt.AffineTransform

cedalion.geometry.registration.register_trans_rot_scale(
coords_target: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
coords_trafo: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
)[source]

Finds affine transformation between coords_target and coords_trafo.

Uses translation, rotation and scaling. Requires at least 3 common labels between the two point clouds.

Parameters:
  • coords_target (LabeledPoints) – Target point cloud.

  • coords_trafo (LabeledPoints) – Source point cloud.

Returns:

Affine transformation between the two point clouds.

Return type:

cdt.AffineTransform

cedalion.geometry.registration.gen_xform_from_pts(p1: ndarray, p2: ndarray) ndarray[source]

Calculate the affine transformation matrix T that transforms p1 to p2.

Parameters:
  • p1 (np.ndarray) – Source points (p x m) where p is the number of points and m is the number of dimensions.

  • p2 (np.ndarray) – Target points (p x m) where p is the number of points and m is the number of dimensions.

Returns:

Affine transformation matrix T.

cedalion.geometry.registration.register_icp(
surface: Surface,
landmarks: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
geo3d: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
niterations=1000,
random_sample_fraction=0.5,
)[source]

Iterative Closest Point algorithm for registration.

Parameters:
  • surface (Surface) – Surface mesh to which to register the points.

  • landmarks (LabeledPoints) – Landmarks to use for registration.

  • geo3d (LabeledPoints) – Points to register to the surface.

  • niterations (int) – Number of iterations for the ICP algorithm (default 1000).

  • random_sample_fraction (float) – Fraction of points to use in each iteration (default 0.5).

Returns:

Tuple containing the losses and transformations

Return type:

Tuple[np.ndarray, np.ndarray]

cedalion.geometry.registration.icp_with_full_transform(
opt_centers: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
montage_points: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
max_iterations: int = 50,
tolerance: float = 500.0,
)[source]

Perform Iterative Closest Point algorithm with full transformation capabilities.

Parameters:
  • opt_centers – Source point cloud for alignment.

  • montage_points – Target reference point cloud.

  • max_iterations – Maximum number of iterations for convergence.

  • tolerance – Tolerance for convergence check.

Returns:

Transformed source points as a numpy array with their coordinates

updated to reflect the best alignment.

np.ndarray: Transformation parameters array consisting of

[tx, ty, tz, rx, ry, rz, sx, sy, sz], where ‘t’ stands for translation components, ‘r’ for rotation components (in radians), and ‘s’ for scaling components.

np.ndarray: Indices of the target points that correspond to each source point as

per the nearest neighbor search.

Return type:

np.ndarray

cedalion.geometry.registration.find_spread_points(points_xr: DataArray) ndarray[source]

Selects three points that are spread apart from each other in the dataset.

Parameters:

points_xr – An xarray DataArray containing the points from which to select.

Returns:

Indices of the initial, farthest, and median-distanced points from the initial point as determined by their positions in the original dataset.

cedalion.geometry.registration.simple_scalp_projection(
geo3d: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
) Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')][source]

Projects 3D coordinates onto a 2D plane using a simple scalp projection.

Parameters:

geo3d (LabeledPoints) – 3D coordinates of points to project. Requires the landmarks Nz, LPA, and RPA.

Returns:

A LabeledPoints containing the 2D coordinates of the projected points.

cedalion.geometry.registration.register_optodes_spring_icp(
scalp: TrimeshSurface,
geo3d: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
channels: list[tuple[str, str]] | Annotated[DataArray, DataArraySchema(dims='time', coords='time', 'time', 'samples')] | DataFrame,
landmarks_scalp: Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')],
nominal_distances: dict[tuple[str, str], float] | None = None,
n_iter: int = 400,
k_spring: float = 1.0,
k_anchor: float = 10.0,
step_size: float = 0.1,
convergence_tol: float = 0.01,
initial_align_mode: str = 'general',
) tuple[Annotated[DataArray, DataArraySchema(dims='label', coords='label', 'label', 'type')], SpringICPResult][source]

Register optode coordinates onto a scalp mesh via spring relaxation and ICP.

Two-phase registration:

  1. Initial alignment — a global transform (translation + rotation + optional scale) is fitted to the matched landmark pairs and applied to geo3d to bring them into the scalp coordinate system.

  2. Spring-relaxation ICP — optodes are iteratively refined:

    • Hooke’s-law springs between each channel-forming source–detector pair, with rest length equal to the nominal inter-optode distance, resist distortion of channel geometry.

    • Strong anchor springs pull any coordinate in geo3d whose label appears in landmarks_scalp toward its known anatomical position on the scalp.

    • After each force step every optode is projected onto the nearest point on the scalp triangulated surface (ICP step).

Parameters:
  • scalp – Triangulated scalp surface in the target coordinate system.

  • geo3d – Probe coordinates (sources, detectors and landmarks) in the probe coordinate system.

  • channels(source_label, detector_label) pairs defining which optode pairs form channels and therefore get a spring.

  • landmarks_scalp – Anatomical landmark positions in the scalp CRS (same CRS as scalp). Any label that also appears in optodes is used as a strong anchor during the relaxation phase.

  • nominal_distances – Optional pre-specified rest lengths for each channel spring, keyed by (src_label, det_label). When None, distances are measured from the phase-1 aligned positions.

  • n_iter – Maximum number of relaxation iterations.

  • k_spring – Spring constant for channel springs. Force magnitude scales linearly with extension from the nominal distance.

  • k_anchor – Spring constant for landmark-anchor springs. Should exceed k_spring to enforce anatomical constraints strongly.

  • step_size – Fraction of the net force vector added to each position per iteration. Reduce if the relaxation is unstable.

  • convergence_tol – Stop early when the maximum surface-projection displacement across all optodes is below this value (in scalp units).

  • initial_align_mode – Phase-1 transform type. One of "trans_rot_isoscale" (7 DOF, default), "trans_rot" (6 DOF), "general" (12 DOF full affine), or "identity" (only adapt units).

Returns:

SpringICPResult with the final optode positions and per-iteration / per-channel quality-control metrics.

Raises:
  • CRSMismatchError – If landmarks_scalp is not in the same CRS as scalp.

  • ValueError – If initial_align_mode is not recognised, or if a channel label is absent from optodes.