biocpd
Coherent Point Drift (CPD) registration in pure NumPy/SciPy with fast variants:
- Rigid and Affine CPD
- Deformable CPD with low-rank (randomized SVD) and k-d tree accelerated E-step
- Constrained Deformable CPD with correspondence priors
- Atlas/SSM-based CPD (
AtlasRegistration) optimized in coefficient space - Opt-in pose-marginalized initialization for severely misaligned atlas inputs
Why biocpd?
- Fast: sparse k-NN E-step, low-rank kernels, and efficient linear solvers
- Flexible: rigid, affine, unconstrained and constrained deformable, and SSM/atlas-based
- Simple: pure NumPy/SciPy implementation; easy to read and extend
Install
pip install -r requirements.txt
# optional (recommended for building)
pip install build wheel
Build wheel
# From repository root
python -m build
# or legacy
python setup.py sdist bdist_wheel
Quickstart
import numpy as np
from biocpd import RigidRegistration, AffineRegistration, DeformableRegistration, ConstrainedDeformableRegistration, AtlasRegistration
rng = np.random.default_rng(0)
X = rng.normal(size=(200, 3)) # target
Y = X + 0.05 * rng.normal(size=(200,3)) # source (noisy)
# Rigid CPD
rig = RigidRegistration(X=X, Y=Y, max_iterations=50, use_kdtree=True, k=10)
TY_rigid, (s, R, t) = rig.register()
# Affine CPD
aff = AffineRegistration(X=X, Y=Y, max_iterations=50, use_kdtree=True, k=10)
TY_affine, (B, t) = aff.register()
# Deformable CPD (scalable low-rank kernel + k-d tree)
defm = DeformableRegistration(X=X, Y=Y, alpha=2.0, beta=2.0, low_rank=True, num_eig=80,
low_rank_method="pivoted_cholesky",
use_kdtree=True, k=10, radius_mode=False, w=0.05,
max_iterations=50)
TY_def, params = defm.register()
# Constrained Deformable CPD
ids = np.arange(10)
con = ConstrainedDeformableRegistration(X=X, Y=Y, alpha=2.0, beta=2.0, low_rank=True, num_eig=80,
low_rank_method="pivoted_cholesky",
use_kdtree=True, k=10, e_alpha=1e-4,
source_id=ids, target_id=ids,
max_iterations=50)
TY_con, params_con = con.register()
# Atlas / Statistical Shape Model CPD
M, D, K = 200, 3, 12
mean_shape = rng.normal(size=(M, D))
U = rng.normal(size=(M*D, K))
L = np.abs(rng.normal(size=(K,))) + 1e-1
atl = AtlasRegistration(X=X, Y=mean_shape, mean_shape=None,
U=U, eigenvalues=L, lambda_reg=0.1,
normalize=True, use_kdtree=True, k=10, radius_mode=False,
optimize_similarity=True, with_scale=True, w=0.02,
max_iterations=50)
TY_atl, params_atl = atl.register()
Pose-marginalized atlas initialization
pose_marginalized_initialization evaluates a deterministic lattice of global
rotation hypotheses, jointly refines pose and shape for the strongest
hypotheses, and returns a warm-start state for AtlasRegistration. It is
strictly opt-in: existing AtlasRegistration construction and registration
behavior are unchanged.
from biocpd import (
AtlasRegistration,
PoseMarginalizedConfig,
)
pose_config = PoseMarginalizedConfig(seed=0)
initial = pose_config.initialize(mean_shape, X, U, L)
atl = AtlasRegistration(
X=X,
Y=mean_shape,
mean_shape=None,
U=U,
eigenvalues=L,
lambda_reg=0.1,
normalize=True,
optimize_similarity=True,
with_scale=True,
)
atl.set_initial_state(
initial.coefficients,
initial.rotation,
initial.scale,
initial.translation,
world_units=True,
)
TY_atl, params_atl = atl.register()
The function API remains available for one-off configuration:
from biocpd import pose_marginalized_initialization
initial = pose_marginalized_initialization(
mean_shape,
X,
U,
L,
rotation_count=193,
seed=0,
)
rotation_count is the exact total hypothesis budget, including identity.
The pose coefficient regularization and outlier defaults are
lambda_reg=0.1 and outlier_weight=0.05, matching the validated real-data
configuration.
By default, every coarse hypothesis receives all eight coarse EM iterations
(coarse_screen_iterations=coarse_iterations=8 and
coarse_survivor_count=rotation_count=193). Set a smaller screen iteration
count and survivor count to opt into staged pruning. Refinement uses the full
source model by default (refine_source_count=None) and at most
refine_target_count target points; every finalist is scored against the
complete source model. Coarse hypotheses use the E-step trajectory objective
by default (coarse_score_mode="trajectory"), which is less sensitive to a
single final coordinate update on symmetric shapes; refined finalists are
always ranked with the exact full-source likelihood. Set
coarse_score_mode="final" to recover final-state coarse scoring. Set
n_jobs above 1 (or to -1 for all detected CPUs) to evaluate independent
hypotheses concurrently.
Pose initialization adds computation before the final atlas registration. Use it when global orientation is uncertain or severe misalignment is expected; skip it for inputs already known to be aligned.
Key options
use_kdtree,k: enable sparse E-step for speed on large datalow_rank,num_eig(deformable): low-rank kernel for fast M-steplow_rank_method(deformable):"randomized_svd"preserves the historical default;"pivoted_cholesky"avoids constructing the full square kernel and is intended for large point setslow_rank_tolerance(deformable): optional residual-diagonal stopping tolerance for pivoted Cholesky; the default0.0uses the requested rank unless the kernel becomes numerically rank deficient- Low-rank deformable M-steps form their weighted coefficient system as a square-root-weighted Gram matrix. This is exact, applies to constrained and unconstrained registration, and requires no additional option.
radius_mode: optional radius gating in sparse E-step (off by default)w: outlier weight (0 ≤ w < 1) in GMMdtype(deformable, constrained deformable, atlas): defaults tonp.float32; setdtype=np.float64when you need the extra precisiondense_block_size(atlas): defaults to a cache-aware block selected from the source and target sizes; pass a positive integer to force a specific blockcoefficient_solver(atlas):"cholesky"preserves the historical direct solve,"cg"uses a warm-started matrix-free solve for atlases with many modes, and"auto"selects CG atcoefficient_auto_thresholdmodescoefficient_tolerance,coefficient_max_iterations(atlas): control the CG residual target and iteration budget; failures fall back to Cholesky and are reported throughcoefficient_solver_diagnosticsnormalize(atlas): improves stability across scalesmean_shape(atlas): withnormalize=True, passmean_shapeas(M, D)
Acknowledgements
- This work builds on the excellent original CPD implementation by Siavash Khallaghi and Anthony Gatti (
pycpd, MIT-licensed) and the CPD method by Myronenko and Song. - Repository for
pycpd: https://github.com/siavashk/pycpd
Citation
If you use this package in academic work, please cite CPD:
- Myronenko, A. and Song, X., "Point Set Registration: Coherent Point Drift," in IEEE Transactions on Pattern Analysis and Machine Intelligence, 2010.
License
MIT
Metadata
Release files for biocpd 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| biocpd-1.3.0.tar.gz | 60.2 kB | Without provenance | Details for biocpd-1.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| biocpd-1.3.0-py3-none-any.whl | Python 3 | none | any | Details for biocpd-1.3.0-py3-none-any.whl |
Total release size: 108.4 kB
Release files / biocpd-1.3.0.tar.gz
| Download URL | biocpd-1.3.0.tar.gz |
|---|---|
| Size | 60.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6b44df811ef58031dfad7887a4bd4458176ff8015f02f633e3b82b2b3bc1735a
|
|
BLAKE2b-256 checksum How to use checksums |
355f6466bbe6b7c18a1a79683f0e04a51264aa56c5f575abf50a7e7a0da8c853
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.20
|
Release files / biocpd-1.3.0-py3-none-any.whl
| Download URL | biocpd-1.3.0-py3-none-any.whl |
|---|---|
| Size | 48.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6fc7e3545956b9a2881fd4a54dcbbabaff1373e58deff2ba041b580bad24f0ac
|
|
BLAKE2b-256 checksum How to use checksums |
a855a7291adc473118ecba619f242bd1b66b417bbd6f5bc82e7bcf0e1dff9f02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.20
|
Security for biocpd 1.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.