vtkProcrustesAlignmentFilter fails for certain geometries

Hello all,

I am trying to align two surfaces derived from medical images of patient blood vessels (actually, the same patient at two time points):

Figure 1

Because of vessel remodeling i.e., dilation due to growth, ICP does not seem appropriate. In fact, these surfaces have already been aligned using some ICP-type algorithm in a commercial software. I’ve read briefly that the Procrustes Alignment algorithm has been used to match disparate geometries with matching landmarks like human faces. But vtkProcrustedAlignmentFilter fails completely when applied to these two geometries.

Specifically, what happens is that the output provides absolutely no change, which seems strange (Figure 2). No change in points is found when comparing the points using the numpy_interface, like below:

>>> (surf2_before_procrustes.Points == surf2_after_procrustes.Points).all()
True

Figure 2: Attempted alignment of two geometries. Left: Original, Middle: After Procrustes, Right: After ICP


To test if I was even using it correctly, I ran both the example from the documentation, and a case where one of the surfaces was manually transformed in ParaView. For both these cases, the filter does work (Figure 3).

Figure 3: Attempted alignment of the same geometry after rigid transformation.

For comparison, I also tried vtkIterativeClosestPointTransform. For the second case (Figure 3, right view), it failed completely. I’ve used it before and the results have been inconsistent. Are there any best practices when performing ICP?

To sum it up, my questions are:

  • Why does vtkProcrustesAlignmentFilter fail completely? Shouldn’t there be even a slight transformation?
  • How similar do the geometries have to be for Procrustes Alignment to work, either in VTK or in general?
  • Does anyone better versed than I am in computational geometry have any better suggestions for aligning these geometries?

Thanks,
Evan Kao

Because it requires n-th point in one pointset correspond to the n-th point in all the other pointsets. If you generate surfaces from two different images then you won’t even have the same number of points. Also note that vtkProcrustesAlignmentFilter only makes sense if you align more than 2 pointsets.

vtkProcrustesAlignmentFilter can align using rigid, similarity, or affine transform, so if the geometries are non-linearly distorted then this filter will not align them.

By extracting a surface mesh from the image, you have lost a lot of information that would have been useful for registration, and it did not make the problem any simpler. So, by extracting vessels you have just made the registration problem harder to solve.

Probably the simplest solution would be to register the original, non-segmented images using intensity-based registration (for example, using Elastix). If you want to register only the extracted vessel tree then you may try to use intensity based registration on distance map images computed from the segmentation results (e.g., using ITK to compute the distance map and then Elastix or ITK to register the images). You can choose either linear or and deformable transforms.

If the vessel trees are very different then you can still register them by specifying matching landmarks in both images and compute a rigid or deformable transform from them (using vtkLandmarkTransform for rigid or vtkThinPlateSplineTransform for deformable alignment).

All these approaches are implemented in 3D Slicer with nice graphical user interface, so you don’t need to write any custom scripts, if you don’t want to. For intensity-based image registration of non-segmented images I would recommend SlicerElastix extension. For registering segmented vessel trees, you can use SegmentRegistration extension (computes distance map and registers them). You can specify matching landmark points and compute registration between them using SlicerIGT extension (using Fiducial Registration Wizard module).

Could you provide a location to get the two models. I did this sort of surface based registration many years ago wit VTK. I’ll try to find my old code and convert it to modern VTK.

Bill

I have some code to test with your data. Please post a link where I can download it.

Hello Andras,

Thanks for clarification on the Procrustes Alignment algorithm and the suggestions. They all seem pretty reasonable and we’ve had some experience SimpleElastix and 3DSlicer’s Elastix extension, although nonrigid registration has always seemed difficult to get right. Part of the reason I didn’t start with image registration was because I didn’t have the images on hand (I didn’t do the segmentation), but I was also worried that I would lose the shape of whatever image is being deformed, especially since the area around the aneurysm is so different. The aneurysm itself starts out relatively flat and oblong, and then becomes more spherical:

Is this worry unfounded? Ideally, I would just twist the centerlines of one the surfaces in such a way to align with the other one while keeping the surface shape mostly the same, which I did try in some manner but the results were mixed.

Here are Google Drive links to the STL surfaces:

Earlier (red) timepoint
Later timepoint

Thanks, for the data. I have code that uses vtkIterativeClosestPointTransform to do the registration. There are some parameters that need tweaking. If this looks promising, I’ll add an example that you can try in the next few days. Here are two screenshots. I tried to duplicate your camera views…
ICP1ICP2