Can I change the color of only certain parts of the mesh?

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.

2 Likes

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.

3 Likes

@RafaelPalomar @cpinter
Fantastic!
I will proceed by referring to your code.