CPD-PyTorch
Coherent Point Drift point set registration in PyTorch
Rigid · Affine · Deformable · Landmark-guided — on CPU, CUDA or Apple GPUs, in float32 or float64
Yuliang (Chris) Xiao · University of Toronto
News · Installation · Quick Start · Methods · Performance · Examples · Documentation · Citation
|
Rigid The bunny, rotated by 45° and shifted: CPD recovers the rotation, translation and scale. |
Affine A fish outline, rotated, sheared and shifted: CPD recovers the full linear map. |
|
Deformable Two different fish: a smooth displacement field bends one onto the other. |
Deformable with landmarks A third of the target is missing; four known point pairs (stars) guide the fit. |
CPD-PyTorch aligns a moving point set Y to a fixed point set X without known
correspondences, using Coherent Point Drift (Myronenko & Song, 2010):
the points of Y are the centers of a Gaussian mixture, fitted to X by
expectation-maximization while the centers move coherently. It keeps the interface of
pycpd and adds GPUs, batches and memory-bounded
computation for point sets of 100,000 points.
News
- 2026-09 — CPD-PyTorch 1.0. A rewrite, published on PyPI as
cpd-pytorch(#3): CUDA and Apple GPUs with adtypeargument for float32 or float64 (#4, building on #2), batched registration, a block-wise E-step that fits 100,000-point sets in under 1 GB, a faster low-rank solver, normalization, correspondences, results verified against pycpd, and documentation on Read the Docs. See the changelog and the migration guide. - 2024-03 — torchcpd 0.0.1. The first installable release: a PyTorch port of pycpd (started in February 2023) with rigid, affine, deformable (including low-rank) and constrained deformable registration on the GPU.
Highlights
- Four methods: rigid (with optional scaling), affine, deformable (with a low-rank option for large point sets) and deformable with known correspondences.
- Any device and precision: CPU, CUDA and Apple Silicon (MPS), in float32 or float64.
- NumPy or PyTorch: NumPy arrays in, NumPy arrays out; or tensors that stay on the GPU.
- Batches: register many point-set pairs in one call.
- Large point sets: memory grows linearly with the number of points, not quadratically.
- Correspondences: the most probable source point for every target point.
- Verified: matches pycpd to 1e-8 in float64; tested on Python 3.8-3.14 with PyTorch 1.10-2.14 on Linux, macOS and Windows, and on CUDA and Apple GPUs.
Installation
pip install cpd-pytorch
For GPU support, install the PyTorch build for your platform first
(instructions). Add matplotlib for the example
scripts with pip install "cpd-pytorch[examples]".
Quick Start
from cpd_pytorch import RigidRegistration, datasets
X, Y = datasets.load_bunny() # target and source, NumPy arrays (N, 3)
reg = RigidRegistration(X, Y)
TY, (s, R, t) = reg.register() # TY = s * Y @ R + t, aligned with X
print(reg.iteration, s, t)
register() returns the transformed source points and the transform, as NumPy arrays
because the inputs were NumPy arrays. To use a GPU, pass device, or tensors that are
already on the GPU:
import torch
reg = RigidRegistration(X, Y, device="cuda", dtype=torch.float32) # NumPy in, NumPy out
X_gpu = torch.as_tensor(X, device="cuda", dtype=torch.float32)
Y_gpu = torch.as_tensor(Y, device="cuda", dtype=torch.float32)
TY, (s, R, t) = RigidRegistration(X_gpu, Y_gpu).register() # tensors on the GPU
On Apple Silicon, use device="mps" with dtype=torch.float32 (MPS has no float64).
Batches, large point sets and correspondences use the same classes:
import numpy as np
from cpd_pytorch import DeformableRegistration
# 32 pairs at once: (B, N, D) and (B, M, D). An unbatched X is shared by every pair.
rng = np.random.default_rng(0)
Ys = np.stack([Y + rng.normal(0, 0.05, 3) for _ in range(32)]) # (32, 453, 3)
TY, (s, R, t) = RigidRegistration(X, Ys).register()
print(TY.shape, R.shape) # (32, 453, 3) (32, 3, 3)
# Low-rank kernel and block-wise E-step for large point sets (add device="cuda" for a GPU),
# then the most probable source point for every target point.
reg = DeformableRegistration(X, Y, low_rank=True, normalize=True)
TY, _ = reg.register()
index = reg.correspondences() # X[n] matches Y[index[n]]
Methods
| Class | Transform | Use it for | Parameters |
|---|---|---|---|
RigidRegistration |
s * Y @ R + t |
Same shape, different pose (and size) | s, R, t |
AffineRegistration |
Y @ B + t |
Shear or anisotropic scaling | B, t |
DeformableRegistration |
Y + G @ W |
Shapes that bend or deform smoothly | G, W |
ConstrainedDeformableRegistration |
Y + G @ W |
Deformation with some known point pairs | G, W |
The options you are most likely to need:
| Option | Default | Meaning |
|---|---|---|
w |
0 |
Weight of the outlier distribution; use 0.1-0.3 for noise, outliers or partial overlap. |
normalize |
False |
Register in normalized coordinates, so the parameters below do not depend on your units. Recommended for deformable registration. |
alpha, beta |
2, 2 |
Deformable only: stiffness of the deformation and width of the Gaussian kernel (in data units, or RMS radii with normalize=True). |
low_rank, num_eig |
False, 100 |
Deformable only: approximate the kernel, for large source point sets. |
max_iterations, tolerance |
100, 1e-3 |
Stopping criteria. |
device, dtype |
from the inputs | Where and in which precision to compute. |
chunk_size |
automatic | Target points per E-step block; lower it if you run out of GPU memory. |
Performance
Time per EM iteration for random 3D point sets of N = M points, after a warm-up and
including the setup of each registration; memory in parentheses. The
benchmarks page has all results, including float64 on
the GPU, the CPU up to 100,000 points and Apple GPUs.
NVIDIA RTX 3090, float32 (peak GPU memory):
| Method | Points | CPD-PyTorch 1.0 | torchcpd 0.0.1 |
|---|---|---|---|
| Rigid | 20,000 | 49 ms (0.8 GB) | 95 ms (10.7 GB) |
| Rigid | 100,000 | 1.2 s (0.8 GB) | out of memory |
| Deformable | 20,000 | 0.42 s (4.6 GB) | 1.37 s (12.2 GB) |
| Deformable, low rank | 20,000 | 59 ms (0.8 GB) | 1.1 s (12.2 GB) |
| Deformable, low rank | 100,000 | 1.4 s (0.8 GB) | out of memory |
Apple M4 Pro CPU, float64 (peak process memory):
| Method | Points | CPD-PyTorch 1.0 | torchcpd 0.0.1 | pycpd |
|---|---|---|---|---|
| Rigid | 8,000 | 69 ms (0.7 GB) | 158 ms (5.4 GB) | 826 ms (5.4 GB) |
| Deformable, low rank | 5,000 | 46 ms (0.5 GB) | 734 ms (2.7 GB) | 1,042 ms (2.6 GB) |
Examples
New to CPD? The quick-start notebook runs in the browser on Google Colab, with no installation.
The examples/ folder has one short script per method (rigid, affine,
deformable, low-rank and constrained, in 2D and 3D) plus batch_registration.py and
large_point_clouds.py. Each runs from any directory and accepts --device, --dtype,
--save animation.gif and --no-show:
python examples/fish_deformable_2D.py --device cpu
Documentation
The documentation has a user guide (methods, parameters, devices, batching,
large point sets), the API reference and a migration guide
for users of pycpd and of torchcpd 0.0.x.
Citation
If you use CPD-PyTorch in your research, please cite both the Coherent Point Drift paper and this library:
@article{myronenko2010cpd,
title = {Point Set Registration: Coherent Point Drift},
author = {Myronenko, Andriy and Song, Xubo},
journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence},
volume = {32},
number = {12},
pages = {2262--2275},
year = {2010},
doi = {10.1109/TPAMI.2010.46}
}
@software{xiao2026cpdpytorch,
author = {Xiao, Yuliang},
title = {{CPD-PyTorch}: Coherent Point Drift Point Set Registration in {PyTorch}},
year = {2026},
version = {1.0.0},
url = {https://github.com/YuliangXiaoYLX/CPD-Pytorch},
license = {Apache-2.0}
}
The software entry is also in CITATION.cff, which GitHub's "Cite this
repository" button reads.
Acknowledgements
CPD-PyTorch started as a PyTorch port of pycpd by
Siavash Khallaghi (MIT License, see NOTICE). Registration with known
correspondences follows the Extended CPD of Golyanik et al. (2016).
License
This project is licensed under the Apache 2.0 License.
Star History
Developed by Chris Xiao | University of Toronto
Metadata
Release files for cpd-pytorch 1.0.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 | |
|---|---|---|---|
| cpd_pytorch-1.0.0.tar.gz | 163.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cpd_pytorch-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 235.9 kB
Release files / cpd_pytorch-1.0.0.tar.gz
| Download URL | cpd_pytorch-1.0.0.tar.gz |
|---|---|
| Size | 163.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
80c1d08659552f6275fdf16699a8f64323cd81e3b1a9550fd8a3e6026751645e
|
|
BLAKE2b-256 checksum How to use checksums |
f196ebce32dfb236c8c8c1f9c55b38138ae897c8153b76ac10e3bf57765245db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / cpd_pytorch-1.0.0-py3-none-any.whl
| Download URL | cpd_pytorch-1.0.0-py3-none-any.whl |
|---|---|
| Size | 72.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ba3c5836fdb0afb7a76cd39309f4384b0d58dcee57ad8639d41ef227deeddd2b
|
|
BLAKE2b-256 checksum How to use checksums |
d769d3cd93ac864905dcb9043ccb54559ca3e15850ddb2b891f88e7eaa0a3b44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency log