Point cloud registration
Align one cloud onto another and get a 4×4 transform. The pipeline follows KISS-Matcher (FPFH plus graph pruning) with the TEASER++ robust solver (GNC-TLS and COTE), so it needs no initial guess and no installation.
Source cloud (to be moved)
The cloud that gets moved. The output matrix applies to it.
Nothing loaded
Target cloud (reference)
The frame that stays put.
Nothing loaded
Registration parameters
Defaults are derived from the cloud size. The voxel size is the knob that matters: it sets the keypoint count, the descriptor neighbourhood and the noise bound all at once.
Drop a match when its two nearest neighbours are too close. Removes most of the wrong matches in repetitive structures.
Runs a nearest-neighbour ICP after the robust solve; usually another order of magnitude off the residual.
Transform T (source → target)
Row-major, p_target = T · p_source. Paste it straight into the coordinate transform tool.
How it works
- Put the source cloud on the left (it gets moved) and the target on the right (it stays fixed).
- The voxel size fills itself in; if the two clouds share a lot of surface with real geometry, leave it alone.
- Hit “Run registration” and check the overlap and RMSE.
- Download the aligned cloud, or paste the matrix into the coordinate transform tool.
The algorithm: KISS-Matcher’s pipeline, TEASER++’s solver
The hard part of registration is not solving for the pose, it is that many of the correspondences are simply wrong. Nearest-neighbour matching on real data is wrong more often than it is right, and a plain least-squares fit follows those outliers all the way to a confidently wrong answer. The whole pipeline is therefore built around throwing out the bad correspondences first, then solving on what is left.
- Voxel downsampling: thins the clouds down to keypoints and, at the same time, fixes the noise bound — every threshold in KISS-Matcher is a multiple of the voxel size.
- FPFH, 33 dimensions: a local geometric histogram per keypoint. Rotate or translate the scene and the values barely move, which is exactly what lets it match across viewpoints.
- Mutual matching plus a ratio test: a pair survives only if it is the nearest neighbour in both directions, and only if its two nearest neighbours are far enough apart. In repetitive structures — corridors, fences — this step alone removes most of the wrong pairs.
- Graph pruning (ROBIN’s max-core): connect correspondences whose pairwise distances agree and keep the largest k-core. True correspondences differ by noise only; wrong ones almost never satisfy that together, so they sink to the low-core layers and get cut.
- GNC-TLS for rotation: a gradually tightened truncated least squares over the chain of differences (TIMs) between corresponding points. The closed-form step is an SVD; the weights separate inliers from outliers by themselves.
- COTE for translation: each axis votes one-dimensionally for the centre covered by the most intervals, so an outlier on one axis cannot drag the other two.
- ICP refinement: the robust solution is already close, so a nearest-neighbour ICP pushes the residual below the voxel scale.
The pipeline comes from MIT-SPARK’s KISS-Matcher (parameter names included: normal radius 3× voxel, FPFH radius 5× voxel, pruning noise bound 1× voxel, solver noise bound 0.75× voxel) and the GNC-TLS / COTE solvers from TEASER++. This page rewrites the same algorithm in TypeScript and runs it in the browser: no WASM download, opens instantly — at the cost of not being the tool of choice at millions of points yet.
Tuning
| Parameter | When to touch it |
|---|---|
| Voxel size | The one knob you have to understand. Too small: many keypoints, slow, and noise dominates the descriptors. Too large: the geometry blurs and matching loses its discriminative power. If the cloud is too big to finish, the page raises it automatically and reports the value it used. |
| Keypoint cap | Controls the cost. 2000–3000 is the comfortable range in a browser. |
| Correspondence cap | Pruning is O(n²), so a higher cap is more accurate and slower. |
| Ratio test | Keep it on for repetitive scenes (long corridors, fences, shelving). Turn it off if the scene is distinctive and matches are scarce. |
| ICP refinement | On by default. If your clouds are very noisy, ICP can pull the result towards the noise — turn it off and compare. |
Reading the result
- Overlap: the share of source points that land within 1.5 voxels of the target. Below 60% usually means it did not really register.
- RMSE: mean nearest-neighbour distance over those points. The two clouds sit on different voxel grids, so the voxel size is the floor — chasing zero is pointless.
- Inliers: how many correspondences the robust solver accepted. Its ratio to the post-pruning count says how clean the data was.
- Rotation / translation: the decomposed, human-readable version. Both near zero means the clouds were already in the same frame.
Things that happen
| Symptom | Usually this |
|---|---|
| Low overlap, matrix looks wrong | The clouds share too little surface, or differ in scale (millimetres versus metres) |
| Very few matches (< 50) | The voxel is so large that detail is gone, or the surface is too smooth |
| The result is flipped 180° | The scene is rotationally symmetric (square room, cylinder); crop the symmetry away first |
| Results jump between runs | Too few inliers — the data does not support global registration; align coarsely or use a method that takes an initial guess |
| The page froze for ten seconds | More than 3000 keypoints, main thread saturated; lower the keypoint cap or raise the voxel size |
Why local only: calibration and registration clouds often carry customer site coordinates or unpublished site data. There is no backend here, so the file never leaves your browser. The trade-off is that your machine sets the compute budget — which is why the defaults lean towards finishing in a few seconds.