How to delete/hide specific points from vtkvolume?

Hi!
I want to make a “scalpel” tool for a vtkVolume where the user can select a specific zone of it, get all of points (from .getPointData) and remove/hide them from the visualization.
Actually, i’m trying to aproaching with two ways:

A: Using clipping planes, but i have the default limit of 6 and i need more than these, exists any way of
create more clipping planes for the vtkVolumeMapper?.

B: Get the scalars of this points and set its values to black or transparent. How can i achieve this?
Actually i’m setting all points i want to be black/not visible for example to a specific value (for example “-3024”) and the other ones with other value (“1000” for example), then i create a new scalarOpacity function with points in this values and add it to the actual volume (remplacing the old one). I need to render other time the volume or do anything more? I don’t if i’m missing something, i let you the code i’m trying here:

    //Here set all values i want to be transparent/black
    const scalars = this.volume3D.getMapper().getInputData().getPointData().getScalars().getData(); 
    for (let i = 0; i < scalars.length; i++) {
       scalars[i] = -3024;
    }
    //Then set all the values i want to remain as it with other value as 1000 (I use a timeout of 1s 
    //between this two loops)
    for(let i = 0; i < points[0].length; i++) {
       scalars[POINT_INDEX] = 1000;
    }
   
   //I also try it with "setScalars" and new array of Float3d but didn't work 
   this.volume3D.getMapper().getInputData().getPointData().getScalars().modified();
  

    //Then add the opacityFunction on the site of the old one: "0". With points as 1.0 in my desired 
    //scalars
     const pieceFun = vtkPiecewiseFunction.newInstance();
     pieceFun.addPoint(999, 0.0);
     pieceFun.addPoint(1000.0, 1.0);
     pieceFun.addPoint(1001.0, 0.0);
     this.volume3D.getProperty().setScalarOpacity(0, pieceFun);

I don’t know if i’m understandly properly the opacity with the scalars or something…
Thanks in advance!

You can create a 3D frustum shape based on the 2D polygon drawn on the screen and the camera parameters, then convert this closed surface to a binary image. This binary image can be either used as a mask in the volume raycast mapper, or you can use it to blank out voxels of the rendered volume.

This is implemented using VTK in 3D Slicer. It is called the Scissors tool (a.k.a. Scalpel in other software):

You can use the feature as is in 3D Slicer using GUI or in your custom C++ modules or Python scripts. Or you can copy-paste the code into your application (Slicer’s permissive license allows using any parts of it for any purpose in any other software). The implementation in C++ is available here, for both slice and 3D views, perspective and parallel projections:

2 Likes

Hi @lassoan ,
I saw this line from about snippet code
vtkMRMLSliceNode* sliceNode = vtkMRMLSliceNode::SafeDownCast(qSlicerSegmentEditorAbstractEffect::viewNode(sliceWidget));

This comes from the MRML lib of Slicer. I wonder if I am using python, (just using VTK) how can I port from those code to python ?

All the classes in Slicer are Python-wrapped, so they are available in Python. Downcasting is not necessary in Python, so the command above looks like this.

Thanks @lassoan ,
I am currently developing pure python application (just using VTK python ). I do not use neither Slicer as executable nor Slicer-python wrapper. Is this possible to port MRML C++ library to pure python code ?

Slicer classes are implemented by n C++ the same way as VTK. You don’t need to build Slicer classes, the same way as VTK, because they are already built for you. The main difference between Slicer (an application framework and libraries) and VTK (a library) that Slicer can set up an application with a user interface and if you want to use these user interface objects then you need to run your script in the Python environment that Slicer provides. This is an advantage if you want to spend less time with developing and maintaining an application framework and GUI. If you already have your own GUI then you can decide to run in in Slicer’s framework; or choose to keep your application separate (and have some redundancy).

All Slicer can be used in outer Python code. Probably you mean if you can use MRML library in any Python environment in the default Python executable. MRML is a standalone library, so the answer is yes, but since we don’t distribute MRML in PyPI yet, you cannot pip install it but you would need to build it yourself. We already provide one Slicer library (vtkAddon) via PyPI and probably MRML is next, but unless we get specific funding to accelerate this work, it will be probably completed in 1-2 years.

Yes I mean use MRML library in any Python environment in the default Python executable. How can I build MRML library into Python library ? Can you guide me the way to build ?

Actually, you may not need to build MRML. Python-wrapped MRMLCore library is already available in the Slicer installation package (MRMLCorePython.pyd). You can import it into your Python environment. To make sure VTK in MRMLCore is compatible with VTK in your Python environment, it could make sense to import VTK from the Slicer install package, too.

A simple way to acheive all these is to run Slicer.exe --cmd, which adds all the necessary paths to your environment and then in that console start Python.exe. You can then access MRML like this:

In [5]: import MRMLCorePython as mrml

In [6]: volumeNode = mrml.vtkMRMLScalarVolumeNode()

In [7]: print(volumeNode)
vtkMRMLScalarVolumeNode (000002898EA08050)
  ID: (none)
  ClassName: vtkMRMLScalarVolumeNode
  Name: (none)
  Debug: false
  MTime: 73
  Description: (none)
  SingletonTag: (none)
  HideFromEditors: false
  Selectable: true
  Selected: false
  UndoEnabled: false
  Node references:
    display [displayNodeRef]: (none)
    storage [storageNodeRef]: (none)
    transform [transformNodeRef]: (none)
  TransformNodeID: (none)
  Spacing: (1, 1, 1)
  Origin: (0, 0, 0)
  VoxelVectorType: undefined
  IJKToRASDirections:
    1 0 0
    0 1 0
    0 0 1

Even simpler is to use PythonSlicer.exe, which is a Python.exe interpreter that also adds MRML and other library paths to the paths (so that you don’t need to run Slicer.exe launcher and then start a Python interpreter).

Hi! Mr.Lasso. Thanks for this code sharing, I notice that code is for convert the 2D polygon to 3D situation, so I can get the colinder inside the volume but I cannot “delete” the part of overlapping, and the function of updateBrushModel seems for just converting the polygon not for rendering the latest result. After times of checking source code in Slicer, maybe the next step is in paintApply, but I still cannot find the final answer from modifySelectedSegmentByLabelmap( Slicer/Modules/Loadable/Segmentations/EditorEffects/qSlicerSegmentEditorPaintEffect.cxx at 024b340baaa191968b6fc954b9f0e548adb40d6d · Slicer/Slicer (github.com)). But it’s not about time to introduce the whole set of MRML in the project.
Could you please tell me how to implicit the overlapping part of the volume and clinder?

All the source code is in the repository. You can find all methods by full-text search in the repository. If your want to see exactly what is done in what order then you can build Slicer in debug mode and step through the code in a debugger.

1 Like