Operating system: window 10
Slicer version: 5.4
hi slicer users,
Is it possible in slicer to select a specific area on the mesh (e.g. by dragging the mouse) and change the color of only that area?
If it is possible, what should I refer to?
Operating system: window 10
Slicer version: 5.4
hi slicer users,
Is it possible in slicer to select a specific area on the mesh (e.g. by dragging the mouse) and change the color of only that area?
If it is possible, what should I refer to?
Hello @dsa934. One option is to associate scalar values to the vertices and enable coloring based on the scalar value. The following is a simple python script that exemplifies how to do that:
import vtk
from slicer import mrmlScene
import slicer
# Create a sphere
sphereSource = vtk.vtkSphereSource()
sphereSource.SetCenter(0.0, 0.0, 0.0)
sphereSource.SetRadius(50.0)
sphereSource.SetPhiResolution(30)
sphereSource.SetThetaResolution(30)
sphereSource.Update()
# Create a model node
modelNode = slicer.vtkMRMLModelNode()
modelNode.SetScene(mrmlScene)
modelNode.SetName("Sphere")
modelNode.SetAndObserveMesh(sphereSource.GetOutput())
# Create a display node
displayNode = slicer.vtkMRMLModelDisplayNode()
mrmlScene.AddNode(displayNode)
modelNode.SetAndObserveDisplayNodeID(displayNode.GetID())
displayNode.SetActiveScalarName("Colors")
displayNode.SetAndObserveColorNodeID("vtkMRMLColorTableNodeRainbow")
displayNode.SetScalarVisibility(True)
# Assign scalar values to represent colors
mesh = modelNode.GetMesh()
scalars = vtk.vtkFloatArray()
scalars.SetName("Colors")
for i in range(mesh.GetNumberOfPoints()):
scalars.InsertNextValue(i / mesh.GetNumberOfPoints())
mesh.GetPointData().SetScalars(scalars)
# Add the model node to the scene
mrmlScene.AddNode(modelNode)
This is the result:
Note how in the left panel the scalar visibility is turned on, the array that should be used as scalar is selected (Colors
), as well as the color table.
I made a module in the past for a very specific case where the main function was this. It never worked 100% reliably but it was possible to use for my purpose.
def paintModelWithSegment(classScalar, parameterNode, currentSegmentID):
modelNode = slicer.util.getNode('Model')
if not modelNode:
return
segmentationNode = slicer.util.getNode('Segmentation')
if not segmentationNode:
return
# Get segment bounds
currentSegment = segmentationNode.GetSegmentation().GetSegment(currentSegmentID)
bounds = np.zeros(6)
currentSegment.GetBounds(bounds)
# Go through each point in model
pointIDsInSegment = []
pointClassArray = modelNode.GetPolyData().GetPointData().GetArray('ArrayName')
sliceViewLabel = "Red" # any slice view where segmentation node is visible works
sliceViewWidget = slicer.app.layoutManager().sliceWidget(sliceViewLabel)
segmentationsDisplayableManager = sliceViewWidget.sliceView().displayableManagerByClassName("vtkMRMLSegmentationsDisplayableManager2D")
points = modelNode.GetPolyData().GetPoints()
for pointIdx in range(modelNode.GetPolyData().GetNumberOfPoints()):
pointRas = points.GetPoint(pointIdx)
if pointRas[0] < bounds[0] or pointRas[0] > bounds[1] or pointRas[1] < bounds[2] or pointRas[1] > bounds[3] or pointRas[2] < bounds[4] or pointRas[2] > bounds[5]:
continue # Point outside simple bounds, skip
# Get segment IDs at vertex position
segmentIDsAtPoint = vtk.vtkStringArray()
segmentationsDisplayableManager.GetVisibleSegmentsForPosition(pointRas, segmentationNode.GetDisplayNode(), segmentIDsAtPoint)
for idIndex in range(segmentIDsAtPoint.GetNumberOfValues()):
if segmentIDsAtPoint.GetValue(idIndex) == currentSegmentID and int(pointClassArray.GetValue(pointIdx)) != classScalar:
pointIDsInSegment.append(pointIdx)
if not pointIDsInSegment:
logging.error(f'Failed to paint. Bounds: {bounds}') #TODO: Remove if works fully reliably
# Include selected vertices in painted class
for pointIdx in pointIDsInSegment:
pointClassArray.SetValue(pointIdx, classScalar)
pointClassArray.Modified()
It uses a segment painted in Segment Editor to set scalars to the vertices of the model that are contained in the segment. I connected it to slicer.vtkSegmentation.RepresentationModified
so that it automatically paints after painting.