Automatic centerline

Thanks Andras. For now I’m trying to use it from the Slicer python interactor and that line worked. It now works as expected with:

logic = ExtractCenterlineLogic()

But, once I enter:

logic.run(inputVolume, outputVolume, threshold, True)

I now get:

AttributeError: 'ExtractCenterlineLogic' object has no attribute 'run'

Any ideas?
Apologies, I feel very feeble and don’t like having to ask for so much help but am otherwise completely lost and unable to make progress! My end goal is to simply feed in an already level set thresholded .vtp model and use the auto detect endpoints function to compute the centreline.
Thanks

Hi Andras, do you have any ideas for how to solve this current issue? I am hoping that what I want to do is quite simple but I’m struggling with the programmatic implementation.
As before my end goal is to feed in an already level set thresholded .vtp model and use the auto detect endpoints function to find the endpoints then compute the centreline.
I am able to feed in the .vtp model but I’m struggling with programming of the auto detect endpoints function.
Any assistance you’re able to provide would be very much appreciated.

It looks like the tests have not been updated since an earlier version of the module; the error message is correct that there is no run() method in ExtractCenterlineLogic. There is an extractCenterline() method which takes as inputs surfacePolyData, endPointsMarkupsNode, and a curveSamplingDistance. Here is what I think you need:

segmentationName = 'MySegmentationName' # replace with the name of your segmentation
segmentName = 'MySegmentName' # replace with the name of the segment you want to find the centerline of
segmentationNode = slicer.util.getNode(segmentationName)
segmentID = segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(segmentName)

import ExtractCenterline
extractLogic = ExtractCenterline.ExtractCenterlineLogic()

# Preprocess the surface
inputSurfacePolyData = extractLogic.polyDataFromNode(segmentationNode, segmentID)
targetNumberOfPoints = 5000.0
decimationAggressiveness = 4 # I had to lower this to 3.5 in at least one case to get it to work, 4 is the default in the module
subdivideInputSurface = False
preprocessedPolyData = extractLogic.preprocess(inputSurfacePolyData, targetNumberOfPoints, decimationAggressiveness, subdivideInputSurface)

# Auto-detect the endpoints
endPointsMarkupsNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", "Centerline endpoints")
networkPolyData = extractLogic.extractNetwork(preprocessedPolyData, endPointsMarkupsNode)
startPointPosition=None
endpointPositions = extractLogic.getEndPoints(networkPolyData, startPointPosition)
endPointsMarkupsNode.RemoveAllMarkups()
for position in endpointPositions:
  endPointsMarkupsNode.AddControlPoint(vtk.vtkVector3d(position))

# Extract the centerline
centerlineCurveNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsCurveNode", "Centerline curve")
centerlinePolyData, voronoiDiagramPolyData = extractLogic.extractCenterline(preprocessedPolyData, endPointsMarkupsNode)
centerlinePropertiesTableNode = None
extractLogic.createCurveTreeFromCenterline(centerlinePolyData, centerlineCurveNode, centerlinePropertiesTableNode)

After you run this, the centerline should be in centerlineCurveNode, and the auto-detected endpoints should be in endPointsMarkupsNode. I don’t know if the preprocessing step is strictly necessary (it seems like you may know more about the inner workings of VMTK than I do), but it seems recommended. To get this code snippet, I tried to pull the relevant lines out of a few functions in the ExtractCenterlineLogic class (SlicerExtension-VMTK/ExtractCenterline.py at 3787ea4a300da28ec5f0824f0715f2713b631155 · vmtk/SlicerExtension-VMTK · GitHub). I have not tried running the auto-detect endpoints this way, but I have done the centerline extraction with supplied endpoints using VMTK using the above methods in one of my own python modules.

3 Likes

Mike, thank you so much!
I’m having a few hiccups getting this to completely work but it is a very useful starting point and I’m much further along than I was before, so thank you. I’m working to get it all ironed out now and hopefully have my first automated centerlines produced soon :slight_smile:
Thanks again

1 Like

Another approach I have used is with a surface mesh that is open at each place I would like there to be centerline seed. vmtk can detect the centroid of each open region and provide the seed location.

1 Like

This discussion was quiet helpful for me.
But i get stuck at the part where teh slicer environment is used.
I already have a quiet sufisticated virtual environment for a project and i also need the automatic centerline extraction.

Is there a way to get “slicer” into a already existing venv, or just use parts of the extionsion?

or is it possible to pass the pype the point coordinates manually?

You can’t directly use Slicer’s python modules in another python interpreter, but it should be easy to move data back and forth through files or other methods.

Thanks! This is working quite well on my end.

I have one issue though.

In some test models - when I am using the “manual” centerline method with the Slicer UI. When I click the Auto-detect button for the points. The first time it will put the points at not so great locations. But when I press the button again it puts the endpoints at the correct locations.
(And when I press again a few times - always the same location as the second one)

Now when I use the script - it puts the endpoints at the location as when I clicked the button the first time. How can I “repeat” the endpoint selection so that it puts them like the second button click?

I already tried to just repeat the code but that did not work.

Sorry, I’m not sure why this happens. I guess maybe the first time the endpoints are located on a non-preprocessed surface, and the pre-processed surface is also generated, then maybe for the second click the endpoints are located using the preprocessed surface? If that were the case, then you might be able to get the result you want by doing the pre-processing step before finding the endpoints, and then making sure to include the pre-processed surface as an input. If that’s not what is going on, then I don’t really have any other ideas about what might cause that behavior.

Okay understood - I will try around a bit more.

One other question - right now the script is using the autodetect points feature.
How can I specify my own point coordinates in there?
Do they need to be in a specific format?

I was able to place the endpoints like this now:

endpoint1 = [16.676, 40.6308, 68.4079]
endpoint2 = [-17.6128, -45.0295, -65.3733]

selected_endpointPositions = [endpoint1, endpoint2 ]

selectedEndPointsNode = slicer.mrmlScene.AddNewNodeByClass(“vtkMRMLMarkupsFiducialNode”, “Selected Endpoints”)

selectedEndPointsNode.RemoveAllControlPoints()
for position in selected_endpointPositions:
selectedEndPointsNode.AddControlPoint(vtk.vtkVector3d(position))

centerlinePolyData, voronoiDiagramPolyData = extractLogic.extractCenterline(preprocessedPolyData, selectedEndPointsNode)

But I am getting the following error:

[VTK] Warning: In vtkvmtkSteepestDescentLineTracer.cxx, line 213

[VTK] vtkvmtkSteepestDescentLineTracer (00000201FB689510): Can’t find a steepest descent edge. Target not reached.

When I select points manually from the dropdown in the UI it works just fine.
Is there something I am missing?

EDIT: Actually it seems it can only detect the network curve when I try it manually?
(how can I do it with python?)

But not the “Tree curve”?

I would recommend that you take a look at exactly how it is done in ExtractCenterline.py. I know that the final centerline endpoints lie on the voronoi medial surface even when the input endpoints are not on this surface; perhaps there is a preprocessing step which finds the closest points on the voronoi medial surface to your selected endpoints and substitutes those before passing to extractLogic.extractCenterline(). All the steps in the processing that you can achieve via the GUI are definitely possible to achieve using python, because the module the GUI is running is a python scripted module. I don’t have time to dig into the code at the moment, so these are just suggestions from memory which I hope can be helpful to you. I think the most productive path will be for you to examine ExtractCenterline.py in detail. Good luck!