Slicer developing a stats module plugin
Copying here from 240905-2052 Developing a 3D Slicer extension because semi-private?
Writing a statistics (sub)module
Registering my stats plugin
- There’s a statistics module: Segment statistics — 3D Slicer documentation
- one can subclass a file to create a similar one:Slicer/Modules/Scripted/SegmentStatistics/SegmentStatisticsPlugins/SegmentStatisticsPluginBase.py at main · Slicer/Slicer
After some trial and error, emulating Slicer/Modules/Scripted/SegmentStatistics/SegmentStatistics.py:
- create a scripted module, normal one
- to which in a directory I’ve added bascially a copy of Slicer/Modules/Scripted/SegmentStatistics/SegmentStatisticsPlugins/LabelmapSegmentStatisticsPlugin.py, I shall call it plugin.
- It has to subclass SegmentStatistics/SegmentStatisticsPlugins/SegmentStatisticsPluginBase.py to be registered
TO REGISTER my plugin, I did
from SegmentStatistics import SegmentStatisticsLogic
from Plugins.myPlugin import MyPlugin
#
def registerNewStats():
SegmentStatisticsLogic.registerPlugin(MyPlugin())
# ...
class myScriptedModule(ScriptedLoadableModule):
"""Uses ScriptedLoadableModule base class, available at:
https://github.com/Slicer/Slicer/blob/main/Base/Python/slicer/ScriptedLoadableModule.py
"""
# Additional initialization step after application startup is complete
slicer.app.connect("startupCompleted()", registerSampleData)
slicer.app.connect("startupCompleted()", registerNewStats)
- TODO
- Do I need to add the plugin to CMakeLists? Slicer/Modules/Scripted/SegmentStatistics/SegmentStatisticsPlugins/CMakeLists.txt at main · Slicer/Slicer
Hiding the UI of the scripted plugin
- I don’t want the UI to be there because the module should just do registration. There’s no I/O involved, it’s the Plugin that does everything
- Hiding a module GUI sample: Slicer/Modules/Scripted/DICOMPlugins/DICOMScalarVolumePlugin.py at 022a4f1a347ebb24509a50f6c8928ae26d3c5955 · Slicer/Slicer
self.parent.hidden = True
- A module actually doesn’t need to have one!
- I can comment out everything except the first class and it works! No logic, tests, UI, parameter nodes, etc.
Interactions between my Plugin and the main SegmentationStatistics module
In the main module:
-
getMeasurementInfo(key)1 iterates through plugins till it finds one that provides the statkey, then itsplugin.getMeasurementInfo(key), this gives it the metadata about that measurement. -
The Plugin should implement that and
computeStatistics(segmentID)that returns a dictionary -
createMeasurementInfo2 is about creating machine-readable metadata, the DICOM stuff is optional I think
Problems
- Is this really one stat per segment? So if I want to do more complex stuff I can’t
- BUT still this approach is the most useful wrt. calculating per-segment stats in a table, then I can give this table to a third module inside the same extension!
- Still unsolved then: the NAMES of the segmentations
Colors
- ACTUALLY — If I create a color file, import it, then I convert the segmentation to a labelmap volume, apply colors to it, then import back, I get a segmentation with the correct names!
- color nodes are
vtkMRMLColorNode, I can attach it to a segmentation prolly - I can create one as
colorNode = slicer.util.loadColorTable('c:/tmp/tmp/Segmentation-label_ColorTable.ctbl')3 - color files syntax is Colors — 3D Slicer documentation
- color nodes are
# Color table file C:/Users/andra/OneDrive/Projects/SlicerTesting2022/20220109-ColorLegend/Segmentation-label_ColorTable.ctbl
# 4 values
0 Background 0 0 0 0
1 artery 216 101 79 255
2 bone 241 214 145 255
3 connective_tissue 111 184 210 255
OK ColorTableNode is where this is (and the colors can be seen), ColorTableStorageNode is the file. BUT ColorNode is used in the scripts. ??
table is child of not-table but I can use them both identically I think
Filling holes
Fills all internal holes inside segments. For example, fills in empty regions inside vertebral bodies segmented with Grow from seeds with intensity-based masking. is awesome and shows many cool things.