Making landmark-based warping transformation faster


(Bakkari) #1

Hi, what is the Python library which I can use in order to speed up a 3D Slicer script?
Many thanks in advance.


(Andras Lasso) #2

Optimization is always very application-specific. What would you like to do?


(Bakkari) #3

Hi Andras. Thank you for your reply.
I created a new function on the script in order to update the transformation of the surface mesh Markups_Trans0 based on the deformation of the 3D Markups as below:



 def updateTpsTransform(caller, eventid):
            numPerEdge = self.fromFids.GetNumberOfFiducials()
            if numPerEdge != self.toFids.GetNumberOfFiducials():
                print
                'Error: Fiducial numbers are not equal!'
                return

            fp = vtk.vtkPoints()
            tp = vtk.vtkPoints()
            f = [0, 0, 0]
            t = [0, 0, 0]

            for i in range(numPerEdge):
                self.fromFids.GetNthFiducialPosition(i, f)
                self.toFids.GetNthFiducialPosition(i, t)
                fp.InsertNextPoint(f)
                tp.InsertNextPoint(t)

            tps = vtk.vtkThinPlateSplineTransform()
            tps.SetSourceLandmarks(fp)
            tps.SetTargetLandmarks(tp)
            tps.SetBasisToR()
            self.tNode.SetAndObserveTransformToParent(tps)

        self.toFids.AddObserver(vtk.vtkCommand.ModifiedEvent, updateTpsTransform)

However, the manual transformation is very slow.


(Andras Lasso) #4

You would need to use profiling to determine where the perfomance bottleneck is then improve that.

If you don’t know how to do profiling then you can try a few trivial things blindly:

1/ Use Fiducial registration wizard module (in SlicerIGT extension), to compute the transform. It does the same as your code snippet but implemented in C++, so it may be faster (especially the conversion from markups to vtk points).

  1. You should also decimate the transformed model using Surface toolbox module as much as possible. Usually you can reduce the number of points by 80-90% without visible difference.

  2. If you transform models then, set transform from parent (and swap source/target landmarks), since that is the forward direction when transforming models or points (transform to parent is the forward direction when transforming images, since image warping requires resampling transform).

  3. Simplify visualization as much as possible. For example, if you showing model/slice intersection is not essential then hide that.


(Tamas Ungi) #5

If you run the original code from the SlicerIGT website, that transforms a 3D grid, does it run fast?

If no, then your computer should probably be upgraded. That code runs fast on average computers.

If yes, then your surface model is too large for the Slicer MRML event system. I had such a problem ealier and the solution was to keep the surface model in a vtkPolyData (not in a vtkMRMLModelNode). Transform the vtkPolyData with the TPS transform you compute at each update, using only VTK functions. Then set this transformed vtkPolyData as the polydata of a vtkMRMLModelNode. Essentially, you prevent Slicer from doing the model transformation for you, because you provide an already transformed model for the MRML system. I could achieve 10x faster rendering this way, but I had several large models, so may experience less improvement if you only have one model.


(Bakkari) #6

Thank you for the help. Could I use Cython in my script?
Is there any way for 3D Slicer to accept pyx file extensions as modules?

Thank you in advance.


(Jean Christophe Fillion Robin) #7

Considering that (1) a .pyx file is compiled by Cython to a .c file, containing the code of a Python extension module and that (2) the .c file is compiled by a C compiler to a .so file (or .pyd on Windows) which can be import -ed directly into a Python session, Slicer has no way to directly import pyx files. This would require the user to have a compiler available.

While there are way to make this happen, it would not be straightforward across all platforms. That said, after we transition to python 3. This will be easier as the official compiler for Slicer distribution will match the one of Python official distribution.

After trying the suggestion of @ungi, I suggest you (1) identify the bottleneck, (2) look for equivalent optimized functions or (if there are none), write the function in a module logic in c++ and expose it to python.


(Andras Lasso) #8

You don’t need to use Cython, you can directly implement any part of your module in C++. Either as a loadable module (for real-time interactions and custom GUI) or as a CLI module - see more information here.


(Bakkari) #9

Thank you for your detailed explanation. Would it be possible to use Boost lib in order to write the function in a module logic in C++ and to expose it to Python?
Could you please show any example of a function which was written and exposed to Python and adopted in a 3D Slicer ?
Thank you in advance.


(Andras Lasso) #10

Yes, sure you can use any C++ library. Boost is library is huge, so probably the simplest is to just copy what you need using bcp into your module’s Logic.

All module logic, widget, and MRML classes are Python-wrapped, so you can use any of them as example (you can see that in the example module that Extension Wizard generates). You can get the module logic from Python by calling logic() method of the module object, for example to get Volume Rendering module’s logic: volRenLogic = slicer.modules.volumerendering.logic().