ICP translation problem

I am currently using the ICP (Iterative Closest Point) algorithm provided by the VTK library. For testing purposes, I collected a set of points from the surface of my object and applied vtkIterativeClosestPointTransform to register these points with the object’s surface. Since there is no expected transformation (rotation or translation), I anticipated that the resulting transformation matrix would be an identity matrix.

However, instead of an identity matrix, the algorithm produced a matrix with a noticeable translation error: [0.33, -0.17, -0.07]. I am curious why this is happening. Shouldn’t the algorithm terminate early, detecting no need for rotation or translation, and return the identity matrix in such cases?
Screenshot from 2024-09-13 06-24-06

(I have a cylinder shape object)

my code:

icp2 = vtkIterativeClosestPointTransform()
target = model_vtk
source = utils.numpy_points_to_vtk_polydata(check_points_array)
icp2.SetSource(source)
icp2.SetTarget(target)
icp2.GetLandmarkTransform().SetModeToRigidBody()
icp2.DebugOn()
icp2.SetMaximumNumberOfIterations(50)
icp2.CheckMeanDistanceOn()
icp2.SetMaximumMeanDistance(0.00001)
icp2.Modified()
icp2.Update()
m = icp2.GetMatrix()
print(m)

You can have a look here. The algorithm terminates after the first iteration if all the points are on the surface.

The code snippet helps, but you need to post a full example and test data if you want to give a chance to community members to reproduce your problem. Without that, it’s just guessing:

  • A cylinder is rotationally symmetric, so there are many good solutions. A solution with zero translation and rotation can be just as good as one with large translation and rotation.
  • You might have mixed up source and target. They are not interchangeable: one is a surface, the other is a pointset.
  • You may have “collected” the points at radius distance from the centerline, which is not exactly the cylinder source output (the polydata has flat faces).
  • Cylinder source generates a mesh optimal for visualization, but the extremely long triangles are ill-suited for any computation (may lead to numerical instability or inaccuracy). Adjust parameters or subdivide the surface to avoid such issues.
  • Maximum number of iterations may be too low.

There are many more potential root causes even for a simple case like this - there are many more nuances if you want to do registration on real-world objects. If you want to find out what’s happening exactly you can build VTK in debug mode, attach a debugger, and go through the code step by step.

Hello,

The VTK ICP uses the vertices to register (no face). Your cylinder might not have any vertex on its side but only on its extermity circles. So you are trying to register random points on the cylinder to two 2D circles of points, that must be why it fails.

If this doesn’t help, don’t hesitate to share the data directly.

Regards,

Julia

VTK ICP matches “each vertex in one surface with the closest surface point on the other” (see documentation). It is implemented by using a locator to get the closest point on surface.

when I created the cylinder, I considered that. I attached the picture of the point cloud here.

so It cant be the problem.
@lassoan and @Julia_Sanchez , In case of investigating more I attached also the data I have. I have two set of points collected from the surface of the cylinder. one is called fine points and the other is rough. I merged this points as one set and do the registration with it.
out_poly_LF_1045_cilynder.vtk (3.9 MB)
fine_cylinder_1045.vtk (850 Bytes)
rough-cylinder_1045.vtk (412 Bytes)

Also here is my complete code.
test_vtk.ipynb (9.6 KB)

Thank you for your help. I’ve gone through the code, though I’m not very proficient in C or C++.

Based on the MaximumMeanDistance, I believe the algorithm may terminate after a few iterations. Since I collected the data from the surface of the object (using ParaView tools) and there is no actual transformation, as you mentioned, it should ideally terminate after just one iteration. However, when I set MaximumMeanDistance(0.0001), it seems the algorithm goes through several iterations before it exits. and it exits with high translation error.

I wanted to check another thing also. if checkMeanDistance is activated, the mean distance between the points before and after transformation is calculated? or the mean distance between the target and new transferred points?

The real shape I am working with is a bone (had same problem with translation with it), but I created a cylinder for simplicity to troubleshoot the problem.

As you’ll see in the code I attached to your previous response, I ensured that I didn’t mix the source points with the target surface.

I’ve attached both the codes and the points and surface files in @Julia_Sanchez replay answer.

Thanks for sharing the data. The landmarks points are not exactly on the surface of the cylinder. This explains why you don’t get exactly identity matrix as a result. It seems that ParaView uses a picker that is probably quick but not very accurate. If you want to get exact results then you can use a locator to find the closest point for each picked point that is actually on the cylinder surface.

I would not worry too much about accuracy of ICP. Instead, the main concern with ICP is that your initial guess is inaccurate and ICP converges to some local optimum, which may be centimeters away from the global optimum. Setting up simulated bone surface probing, with simulated error, etc. is a good approach for exploring this, just keep going.

Exactly, I realized about an hour ago that some points weren’t perfectly on the surface. Once I corrected them, everything worked fine. But shouldn’t the algorithm still work even with noisy source data? I tried the same input with Open3D, and it returned an almost identity matrix. That’s why I initially ruled out the idea of having incorrect data points.