I have several polygons that are parallel to each other and spaced at certain intervals. These are of type “model” and their mesh type is Surface Mesh (vtkPolyData). This data has been placed along with CT in [Google Drive]. An image of these models is shown below.
It is obvious that the Number of Points, Number of Cells, and Volume may vary between them. Now I want to create a new Surface Mesh from these Surface Meshes in a way that its boundaries consist of the initial and final polygons of this set, and its body is composed of these polygons.
Can this be implemented using the available functions in VTK and in Python?
Set the appended polydata as the Planar contour representation of the segmentation node
Call GetClosedSurfaceRepresentation on the segmentation node. Then the underlying conversion rule will do this interpolation for you
Note that you will need the SlicerRT extension to be installed first, because the mentioned conversion rule is provided there.
It is probably useful to check out the third figure in the page Image Segmentation — 3D Slicer documentation. You will see that this type of representation is often the original input when you work with radiation therapy data. This interpolator has been stable for quite a while now, so you should be able to use it safely.
I’ve completed up to the second step of thinking, and I wasn’t successful in implementing the third step… Hopefully, I’ll be able to continue the work next week…
The following lines are my code at this point.
import vtk
import slicer
import vtkSegmentationCorePython
nodeNames = ["Model_-407.5", "Model_-405.0", "Model_-400.0", "Model_-395.0", "Model_-390.0", "Model_-385.0", "Model_-380.0", "Model_-375.0", "Model_-372.5"]
appendFilter = vtk.vtkAppendPolyData()
for nodeName in nodeNames:
node = slicer.util.getNode(nodeName)
if node:
# Get the vtkPolyData from the node
polyData = node.GetPolyData()
if polyData:
# Add the polyData to the appendFilter
appendFilter.AddInputData(polyData)
appendFilter.Update()
outputPolyData = appendFilter.GetOutput()
newModelNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode", "outputPolyData")
newModelNode.SetAndObservePolyData(outputPolyData)
segmentationConverter = vtkSegmentationCorePython.vtkSegmentationConverter()
planarContourName = segmentationConverter.GetSegmentationPlanarContourRepresentationName()
print("Planar contour representation name:", planarContourName)
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
First, I must thank Dear Csaba Pinter for the guidance provided in solving the issue I raised. In implementing this guidance, I encounter two issues:
First Issue:
Executing the above commands in the Python Console in 3DSlicer (repeated below) creates a node named “Segmentation”.
import vtk
import slicer
import vtkSegmentationCorePython
nodeNames = ["Model_-407.5", "Model_-405.0", "Model_-400.0", "Model_-395.0", "Model_-390.0", "Model_-385.0", "Model_-380.0", "Model_-375.0", "Model_-372.5"]
appendFilter = vtk.vtkAppendPolyData()
for nodeName in nodeNames:
node = slicer.util.getNode(nodeName)
if node:
# Get the vtkPolyData from the node
polyData = node.GetPolyData()
if polyData:
# Add the polyData to the appendFilter
appendFilter.AddInputData(polyData)
appendFilter.Update()
outputPolyData = appendFilter.GetOutput()
newModelNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode", "outputPolyData")
newModelNode.SetAndObservePolyData(outputPolyData)
segmentationConverter = vtkSegmentationCorePython.vtkSegmentationConverter()
planarContourName = segmentationConverter.GetSegmentationPlanarContourRepresentationName()
print("Planar contour representation name:", planarContourName)
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
As seen in the following figure, its Representation type is Binary labelmap, not Planar contour. I expected that by executing the above commands, the Representation type would be Planar contour.
How can I set the Representation type to Planar contour according to the provided guidance by Csaba Pinter?
Second Issue:
In implementing the third step, Csaba Pinter told that:
Set the appended polydata as the Planar contour representation of the segmentation node…
I have a problem. I can do this by setting Planar contour and importing outputPolyData in Segmentations module (not in Python Console of 3DSlicer), but I encounter the following error. How can I perform this third step?
The code seems so far so good, but not complete. As you said you’ll need to set the proper source representation, and then the representation itself. By the way I suggest using the latest Slicer, 5.2.2 is now years old, and the API has changed a bit since then too (e.g. “master” vs “source” representation).
segmentationNode.GetSegmentation().SetSourceRepresentationName(planarContourName)
seg = slicer.vtkSegment()
seg.SetName('NameYouWant')
seg.AddRepresentation(planarContourName, newModelNode.GetPolyData())
segmentationNode.GetSegmentation().AddSegment(seg)
A few comments:
You don’t need import vtkSegmentationCorePython, you can simply do slicer.vtkSegmentationConverter. Also, you don’t need to instantiate it, so just slicer.vtkSegmentationConverter.GetSegmentationPlanarContourRepresentationName()
You don’t need to create a new model node. Just have the poly data. So in the above snippet, you can replace newModelNode.GetPolyData() with appendFilter.GetOutput()
Unfortunately, I don’t understand why I encountered this error. I even used the CreateClosedSurfaceRepresentation() method because it was explained in relation to GetClosedSurfaceRepresentation that:
“Get a segment as a surface mesh. If representation does not exist yet then CreateClosedSurfaceRepresentation() must be called before this method. This function returns a copy of the segment closed surface in outputClosedSurface. Returns true on success.”
As mentioned in the help on built-in function GetClosedSurfaceRepresentation:
GetClosedSurfaceRepresentation(...) method of MRMLCorePython.vtkMRMLSegmentationNode instance
GetClosedSurfaceRepresentation(self, segmentId:str,
outputClosedSurface:vtkPolyData) -> bool
C++: virtual bool GetClosedSurfaceRepresentation(
const std::string segmentId, vtkPolyData *outputClosedSurface)
Get a segment as a surface mesh. If representation does not exist
yet then CreateClosedSurfaceRepresentation() must be called
before this method. This function returns a copy of the segment
closed surface in outputClosedSurface. Returns true on success.
As mentioned above, If the representation does not exist, the function CreateClosedSurfaceRepresentation must be called before using this method. Excuse me, but I didn’t understand why Csaba Pinter mentioned that the representation type should be Planar contour in guidance. Anyway, I continued my efforts according to this help (although I believe this is not correct):
As mentioned above, the problem is related to the first argument of the CreateClosedSurfaceRepresentation method.
Excuse me… How can I set the two arguments of GetClosedSurfaceRepresentation so that it creates and covers all contours of my models (Model_-407.5, Model_-405.0, ..., Model_-372.5) with a solid model?
During my attempt to call GetClosedSurfaceRepresentation, I encountered an interesting coincidence. Upon executing the following codes, when I run the commands the section of Step #4, 3DSlicer suddenly crashes and closes.
import vtk
import slicer
# Step #1
nodeNames = ["Model_-407.5", "Model_-405.0", "Model_-400.0", "Model_-395.0", "Model_-390.0", "Model_-385.0", "Model_-380.0", "Model_-375.0", "Model_-372.5"]
appendFilter = vtk.vtkAppendPolyData()
for nodeName in nodeNames:
node = slicer.util.getNode(nodeName)
if node:
# Get the vtkPolyData from the node
polyData = node.GetPolyData()
if polyData:
# Add the polyData to the appendFilter
appendFilter.AddInputData(polyData)
appendFilter.Update()
outputPolyData = appendFilter.GetOutput()
newModelNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode", "outputPolyData")
newModelNode.SetAndObservePolyData(outputPolyData)
# Step #2
segmentationConverter = slicer.vtkSegmentationConverter()
planarContourName = slicer.vtkSegmentationConverter.GetSegmentationPlanarContourRepresentationName()
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
# Step #3 (the guidance of Csaba Pinter)
segmentationNode.GetSegmentation().SetSourceRepresentationName(planarContourName)
seg = slicer.vtkSegment()
seg.SetName('bolusSegment')
seg.AddRepresentation(planarContourName, appendFilter.GetOutput())
segmentationNode.GetSegmentation().AddSegment(seg)
# Step #4
segmentation_node = slicer.util.getNode('Segmentation')
segment_id = segmentation_node.GetSegmentation().GetSegmentIdBySegmentName('bolusSegment')
segmentation_node.CreateClosedSurfaceRepresentation()
segmentation_node.GetClosedSurfaceRepresentation(segment_id, outputPolyData)
I think that I am near to implementing of the fourth step (with the minor error)
Please guide me to solve it.
Thanks a lot.
Best regards.
Shahrokh