Hi again,
In the end, I repeated the process computing the normals and passing the vtkCleanPolyData
filter and everything works as expected now, I am not sure of what I was doing wrong before. In the end, the code looks something like this (I paste here from the vtkPolyData
definition onwards):
# Define vtkPolyData from selected points (vtkPoints object) and polys (vtkCellArray object)
circularSegment = vtk.vtkPolyData()
circularSegment.SetPoints(pointsCircularSegmentModel)
circularSegment.SetPolys(cellArrayCircularSegmentModel)
# We can to compute the normals for all mesh triangles
normals = vtk.vtkPolyDataNormals()
normals.SetInputData(circularSegment)
normals.SetFeatureAngle(80)
normals.AutoOrientNormalsOn()
normals.UpdateInformation()
normals.Update()
circularSegment = normals.GetOutput()
# Clean the vtkPolyData. Without this, normal edges are seen as non-manifold and boundary edges as manifold. Normals have to be computed prior to this
cleanPolyData = vtk.vtkCleanPolyData()
cleanPolyData.SetInputData(circularSegment)
cleanPolyData.Update()
circularSegment = cleanPolyData.GetOutput()
# Now we can extract the boundary edges with the vtkFeatureEdges filter
edgesFilter = vtk.vtkFeatureEdges()
edgesFilter.BoundaryEdgesOn()
edgesFilter.FeatureEdgesOff()
edgesFilter.ManifoldEdgesOff()
edgesFilter.NonManifoldEdgesOff()
edgesFilter.SetInputData(circularSegment)
edgesFilter.Update()
boundaryEdges = edgesFilter.GetOutput()
# The vtkConnectivityFilter allows for separation of both boundaries (in my case I only have two groups, so I use first and final cell seeds for separation)
connectivityFilter = vtk.vtkConnectivityFilter()
connectivityFilter.SetExtractionModeToCellSeededRegions()
connectivityFilter.AddInputData(boundaryEdges)
connectivityFilter.AddSeed(0)
connectivityFilter.Update()
proximalBorder = connectivityFilter.GetOutput()
connectivityFilter2 = vtk.vtkConnectivityFilter()
connectivityFilter2.SetExtractionModeToCellSeededRegions()
connectivityFilter2.AddInputData(boundaryEdges)
connectivityFilter2.AddSeed(boundaryEdges.GetCell(boundaryEdges.GetNumberOfPoints() - 1).GetPointId(0))
connectivityFilter2.Update()
distalBorder = connectivityFilter2.GetOutput()
Now I continued the code introducing the vtkClipClosedSurface
filter to close the surface at the edges (criteria for plane definition are a bit too adapted at my example, just want it to work altogether):
# We have to define planes for the vtkClipClosedSurface filter. First pool all points from both groups
proximalBorderPoints = np.ndarray([proximalBorder.GetNumberOfPoints(), 3])
distalBorderPoints = np.ndarray([distalBorder.GetNumberOfPoints(), 3])
for idx in range(proximalBorder.GetNumberOfPoints()):
proximalBorderPoints[idx] = proximalBorder.GetPoints().GetPoint(idx)
for idx in range(distalBorder.GetNumberOfPoints()):
distalBorderPoints[idx] = distalBorder.GetPoints().GetPoint(idx)
# Get point with maximal z coordinate in proximal boundary and minimum z coordinate in distal boundary
proximalBorderZMax = np.amax(proximalBorderPoints[:, 2])
distalBorderZMin = np.amin(distalBorderPoints[:, 2])
# Cutting planes definition
proximalPlane = vtk.vtkPlane()
proximalPlane.SetNormal(0, 0, 1)
proximalPlane.SetOrigin(0, 0, proximalBorderZMax)
distalPlane = vtk.vtkPlane()
distalPlane.SetNormal(0, 0, -1)
distalPlane.SetOrigin(0, 0, distalBorderZMin)
clippingPlanes = vtk.vtkPlaneCollection()
clippingPlanes.AddItem(proximalPlane)
clippingPlanes.AddItem(distalPlane)
# Application of the vtkClipClosedSurface filter
clip = vtk.vtkClipClosedSurface()
clip.AddInputData(circularSegment)
clip.SetClippingPlanes(clippingPlanes)
clip.Update()
clippedCircularSegment = clip.GetOutput()
Now the object looks all right:
I am doing all of this in a Slicer context, and now I am trying to store it as a new segment in a vtkMRMLSegmentationNode. I am using the following line:
segmentationNode.AddSegmentFromClosedSurfaceRepresentation(clippedCircularSegment)
But this is not working (it returns an empty string). I have been able to define a new vtkMRMLModelNode and visualize the object in Slicer:
circularSegmentModelNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode")
circularSegmentModelNode.SetAndObservePolyData(clippedCircularSegment)
But I cannot add it as a new segment in the vtkMRMLSegmentationNode, which is what I would like to do, and I don’t really understand why… Any help with this?