Registration Operations
Registration Operations Tutorial.
This tutorial demonstrates global and landmark-based registration operations for aligning multiple objects (volumes, masks, surfaces, volume meshes, measurements, and primitives) to a fixed target.
Prerequisites
- Volvicon application must be running
- Objects to register must be loaded in the project
- Surface-based registration: Point Matching (ICP), Feature Matching, Combined
- Image-based registration: Rigid (6DOF) or Affine (12DOF) for volume-to-volume
- Landmark-based registration with optional fine-tuning
Listing Available Objects​
all_surfaces = app.get_all_surface_names()
all_masks = app.get_all_mask_names()
all_volumes = app.get_all_volume_names()
all_volume_meshes = app.get_all_volume_mesh_names()
print(f"Available surfaces: {all_surfaces}")
print(f"Available masks: {all_masks}")
print(f"Available volumes: {all_volumes}")
print(f"Available volume meshes: {all_volume_meshes}")
Object Types for Registration​
# Objects are identified by name and type using GlobalRegistrationObject:
#
# api.GlobalRegistrationObjectType.Volume (0) - Volume images
# api.GlobalRegistrationObjectType.Mask (1) - Mask/segmentation
# api.GlobalRegistrationObjectType.Surface (2) - Surface meshes
# api.GlobalRegistrationObjectType.VolumeMesh (3) - Tetrahedral meshes
# api.GlobalRegistrationObjectType.Measurement (4) - Measurements
# api.GlobalRegistrationObjectType.Primitive (5) - Primitive shapes
Registration Algorithms​
# Surface Registration Algorithms:
# - api.GlobalRegistrationSurfaceAlgorithm.PointMatching (ICP-based)
# - api.GlobalRegistrationSurfaceAlgorithm.FeatureMatching (Feature-based)
# - api.GlobalRegistrationSurfaceAlgorithm.PointPlusFeatureMatching (Combined)
#
# Image Registration Algorithms (Volume-to-Volume only):
# - api.GlobalRegistrationImageAlgorithm.Rigid6DOF (Rotation + Translation)
# - api.GlobalRegistrationImageAlgorithm.Affine12DOF (Full affine transform)
#
# Registration Modes for Point Matching:
# - api.GlobalRegistrationMode.RigidBody (Rotation + Translation)
# - api.GlobalRegistrationMode.Similarity (+ Uniform Scaling)
# - api.GlobalRegistrationMode.Affine (Full affine)
Global Registration - Surface to Surface (Point Matching / ICP)​
if len(all_surfaces) >= 2:
# Define movable object(s) - objects that will be transformed
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = all_surfaces[0]
movable_obj.type = api.GlobalRegistrationObjectType.Surface
# Define fixed object - the target to align to
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[1]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
# Configure surface registration parameters for Point Matching (ICP)
surface_params = api.SurfaceRegistrationParams()
surface_params.algorithm = api.GlobalRegistrationSurfaceAlgorithm.PointMatching
PointMatchingParams = surface_params.point_matching
PointMatchingParams.mode = api.GlobalRegistrationMode.RigidBody
PointMatchingParams.maximum_landmarks = 1000 # Number of sample points
PointMatchingParams.match_centroids = True # Pre-align centroids
surface_params.point_matching = PointMatchingParams
# Perform registration
GlobalRegistrationResult = RegistrationOperations.global_registration(
[movable_obj], # movable_objects (list)
fixed_obj, # fixed_object
surface_params, # surface_params
api.ImageRegistrationParams(), # image_params (not used for surface)
True # calculate_rmse (bool)
)
print(f"Registration success: {GlobalRegistrationResult.success}")
print(f"RMSE: {GlobalRegistrationResult.rmse_millimeters} mm")
print(f"Transformation matrix: {GlobalRegistrationResult.transformation_matrix}")
Global Registration - Feature Matching​
if len(all_surfaces) >= 2:
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = all_surfaces[0]
movable_obj.type = api.GlobalRegistrationObjectType.Surface
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[1]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
# Configure feature matching parameters
surface_params = api.SurfaceRegistrationParams()
surface_params.algorithm = api.GlobalRegistrationSurfaceAlgorithm.FeatureMatching
FeatureMatchingParams = surface_params.feature_matching
FeatureMatchingParams.global_iterations = 3 # Number of iterations
FeatureMatchingParams.overlap_radius = 10.0 # Search radius in mm
FeatureMatchingParams.curvature_threshold = 0.001 # Accuracy threshold
surface_params.feature_matching = FeatureMatchingParams
GlobalRegistrationResult = RegistrationOperations.global_registration(
[movable_obj],
fixed_obj,
surface_params,
api.ImageRegistrationParams(),
True
)
print(f"Feature matching registration success: {GlobalRegistrationResult.success}")
print(f"RMSE: {GlobalRegistrationResult.rmse_millimeters} mm")
Global Registration - Combined Point + Feature Matching​
if len(all_surfaces) >= 2:
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = all_surfaces[0]
movable_obj.type = api.GlobalRegistrationObjectType.Surface
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[1]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
# Configure combined algorithm parameters
surface_params = api.SurfaceRegistrationParams()
surface_params.algorithm = api.GlobalRegistrationSurfaceAlgorithm.PointPlusFeatureMatching
# Point matching phase
PointPlusFeatureMatchingParams = surface_params.point_plus_feature_matching
PointPlusFeatureMatchingParams.mode = api.GlobalRegistrationMode.RigidBody
PointPlusFeatureMatchingParams.maximum_landmarks = 1000
PointPlusFeatureMatchingParams.match_centroids = True
# Feature matching phase
PointPlusFeatureMatchingParams.global_iterations = 3
PointPlusFeatureMatchingParams.overlap_radius = 10.0
PointPlusFeatureMatchingParams.curvature_threshold = 0.001
surface_params.point_plus_feature_matching = PointPlusFeatureMatchingParams
GlobalRegistrationResult = RegistrationOperations.global_registration(
[movable_obj],
fixed_obj,
surface_params,
api.ImageRegistrationParams(),
True
)
print(f"Combined registration success: {GlobalRegistrationResult.success}")
Global Registration - Volume to Volume (Image Registration)​
if len(all_volumes) >= 2:
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = all_volumes[0]
movable_obj.type = api.GlobalRegistrationObjectType.Volume
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_volumes[1]
fixed_obj.type = api.GlobalRegistrationObjectType.Volume
# Configure image registration parameters
image_params = api.ImageRegistrationParams()
image_params.algorithm = api.GlobalRegistrationImageAlgorithm.Rigid6DOF # or Affine12DOF
image_params.number_of_iterations = 100 # Optimizer iterations
image_params.learning_rate = 0.2 # Step size
image_params.relaxation_factor = 0.5 # Convergence factor
GlobalRegistrationResult = RegistrationOperations.global_registration(
[movable_obj],
fixed_obj,
api.SurfaceRegistrationParams(), # Not used for volume registration
image_params,
True
)
print(f"Image registration success: {GlobalRegistrationResult.success}")
print(f"RMSE: {GlobalRegistrationResult.rmse_millimeters} mm")
Global Registration - Multiple Objects​
# Register multiple movable objects to a single fixed target
if len(all_surfaces) >= 3:
# Create list of movable objects
movable_objects = []
obj1 = api.GlobalRegistrationObject()
obj1.name = all_surfaces[0]
obj1.type = api.GlobalRegistrationObjectType.Surface
movable_objects.append(obj1)
obj2 = api.GlobalRegistrationObject()
obj2.name = all_surfaces[1]
obj2.type = api.GlobalRegistrationObjectType.Surface
movable_objects.append(obj2)
# Fixed target
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[2]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
surface_params = api.SurfaceRegistrationParams()
surface_params.algorithm = api.GlobalRegistrationSurfaceAlgorithm.PointMatching
GlobalRegistrationResult = RegistrationOperations.global_registration(
movable_objects, # List of multiple objects
fixed_obj,
surface_params,
api.ImageRegistrationParams(),
True
)
print(f"Multi-object registration success: {GlobalRegistrationResult.success}")
Global Registration - Mixed Object Types​
# Register different object types together (e.g., surface + mask to a fixed surface)
if all_surfaces and all_masks:
movable_objects = []
# Add a surface
surface_obj = api.GlobalRegistrationObject()
surface_obj.name = all_surfaces[0]
surface_obj.type = api.GlobalRegistrationObjectType.Surface
movable_objects.append(surface_obj)
# Add a mask
mask_obj = api.GlobalRegistrationObject()
mask_obj.name = all_masks[0]
mask_obj.type = api.GlobalRegistrationObjectType.Mask
movable_objects.append(mask_obj)
# Fixed target (surface)
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[1] if len(all_surfaces) > 1 else all_surfaces[0]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
surface_params = api.SurfaceRegistrationParams()
GlobalRegistrationResult = RegistrationOperations.global_registration(
movable_objects,
fixed_obj,
surface_params,
api.ImageRegistrationParams(),
True
)
print(f"Mixed-type registration success: {GlobalRegistrationResult.success}")
Landmark Registration - Basic​
# Align objects using corresponding landmark points (minimum 3 pairs required)
if len(all_surfaces) >= 2:
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = all_surfaces[0]
movable_obj.type = api.GlobalRegistrationObjectType.Surface
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[1]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
# Define corresponding landmark points [x, y, z] in world coordinates
# These should be anatomical or geometric features that correspond between objects
movable_landmarks = [
[10.0, 20.0, 30.0], # Point 1 on movable
[15.0, 25.0, 35.0], # Point 2 on movable
[20.0, 30.0, 40.0], # Point 3 on movable
[25.0, 35.0, 45.0] # Point 4 on movable (optional, more points = better accuracy)
]
fixed_landmarks = [
[0.0, 0.0, 0.0], # Corresponding point 1 on fixed
[5.0, 5.0, 5.0], # Corresponding point 2 on fixed
[10.0, 10.0, 10.0], # Corresponding point 3 on fixed
[15.0, 15.0, 15.0] # Corresponding point 4 on fixed
]
LandmarkRegistrationResult = RegistrationOperations.landmark_registration(
[movable_obj], # movable_objects (list)
fixed_obj, # fixed_object
movable_landmarks, # movable_landmarks (list of [x, y, z])
fixed_landmarks, # fixed_landmarks (list of [x, y, z])
api.SurfaceRegistrationParams(),
api.ImageRegistrationParams(),
False, # perform_global_registration (fine-tuning)
True # calculate_rmse (bool)
)
print(f"Landmark registration success: {LandmarkRegistrationResult.success}")
print(f"Landmark RMSE: {LandmarkRegistrationResult.landmark_rmse_millimeters} mm")
print(f"Transformation matrix: {LandmarkRegistrationResult.landmark_transformation_matrix}")
Landmark Registration - With Global Registration Fine-Tuning​
# Perform landmark registration followed by automatic fine-tuning
if len(all_surfaces) >= 2:
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = all_surfaces[0]
movable_obj.type = api.GlobalRegistrationObjectType.Surface
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = all_surfaces[1]
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
movable_landmarks = [
[10.0, 20.0, 30.0],
[15.0, 25.0, 35.0],
[20.0, 30.0, 40.0]
]
fixed_landmarks = [
[0.0, 0.0, 0.0],
[5.0, 5.0, 5.0],
[10.0, 10.0, 10.0]
]
# Configure fine-tuning parameters
surface_params = api.SurfaceRegistrationParams()
surface_params.algorithm = api.GlobalRegistrationSurfaceAlgorithm.PointMatching
PointMatchingParams = surface_params.point_matching
PointMatchingParams.mode = api.GlobalRegistrationMode.RigidBody
PointMatchingParams.maximum_landmarks = 1000
surface_params.point_matching = PointMatchingParams
LandmarkRegistrationResult = RegistrationOperations.landmark_registration(
[movable_obj],
fixed_obj,
movable_landmarks,
fixed_landmarks,
surface_params,
api.ImageRegistrationParams(),
True, # Enable global registration fine-tuning
True
)
print(f"Landmark + fine-tuning success: {LandmarkRegistrationResult.success}")
print(f"Landmark RMSE: {LandmarkRegistrationResult.landmark_rmse_millimeters} mm")
print(f"Global registration RMSE: {LandmarkRegistrationResult.global_registration_rmse_millimeters} mm")
print(f"Landmark transform: {LandmarkRegistrationResult.landmark_transformation_matrix}")
print(f"Fine-tuning transform: {LandmarkRegistrationResult.global_transformation_matrix}")
Working with Transformation Matrices​
# The transformation matrix is a 4x4 matrix in row-major order (16 elements)
# Format: [m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33]
#
# Standard 4x4 homogeneous transformation matrix:
# | m00 m01 m02 m03 | | Rxx Rxy Rxz Tx |
# | m10 m11 m12 m13 | = | Ryx Ryy Ryz Ty |
# | m20 m21 m22 m23 | | Rzx Rzy Rzz Tz |
# | m30 m31 m32 m33 | | 0 0 0 1 |
#
# Where R is the 3x3 rotation matrix and T is the translation vector
# Example: Extract translation from result
# if result.success:
# matrix = result.transformation_matrix
# translation_x = matrix[3] # m03
# translation_y = matrix[7] # m13
# translation_z = matrix[11] # m23
# print(f"Translation: ({translation_x}, {translation_y}, {translation_z}) mm")
Complete Registration Workflow Example​
# Example workflow: Create test surfaces and register them
def registration_workflow_example():
"""Complete example showing registration workflow."""
global api
global app
global SurfaceOperations
global RegistrationOperations
# Step 1: Create test surfaces
fixed_sphere = SurfaceOperations.create_sphere(
"FixedSphere", 20.0, [0.0, 0.0, 0.0], 32, 32
)
movable_sphere = SurfaceOperations.create_sphere(
"MovableSphere", 20.0, [50.0, 30.0, 20.0], 32, 32
)
print(f"Created: {fixed_sphere}, {movable_sphere}")
# Step 2: Configure registration
movable_obj = api.GlobalRegistrationObject()
movable_obj.name = movable_sphere
movable_obj.type = api.GlobalRegistrationObjectType.Surface
fixed_obj = api.GlobalRegistrationObject()
fixed_obj.name = fixed_sphere
fixed_obj.type = api.GlobalRegistrationObjectType.Surface
surface_params = api.SurfaceRegistrationParams()
surface_params.algorithm = api.GlobalRegistrationSurfaceAlgorithm.PointMatching
PointMatchingParams = surface_params.point_matching
PointMatchingParams.match_centroids = True
surface_params.point_matching = PointMatchingParams
# Step 3: Perform registration
GlobalRegistrationResult = RegistrationOperations.global_registration(
[movable_obj],
fixed_obj,
surface_params,
api.ImageRegistrationParams(),
True
)
# Step 4: Check results
if GlobalRegistrationResult.success:
print(f"Registration successful!")
print(f"RMSE: {GlobalRegistrationResult.rmse_millimeters:.4f} mm")
else:
print("Registration failed")
# Step 5: Clean up (optional)
app.delete_surfaces([fixed_sphere, movable_sphere])
return GlobalRegistrationResult
# # Uncomment to run the workflow:
# registration_workflow_example()
print("Registration operations tutorial completed successfully.")
Related Resources​
- API Reference - API documentation
- Quick Reference - Common methods at a glance