Slicer 5.9
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
SegmentEditorGrowFromSeedsEffect.py
Go to the documentation of this file.
1import logging
2import os
3import time
4
5import qt
6import vtk
7
8import slicer
9from slicer.i18n import tr as _
10
11from SegmentEditorEffects import *
12
13
15 """AutoCompleteEffect is an effect that can create a full segmentation
16 from a partial segmentation (not all slices are segmented or only
17 part of the target structures are painted).
18 """
19
20 def __init__(self, scriptedEffect):
21 AbstractScriptedSegmentEditorAutoCompleteEffect.__init__(self, scriptedEffect)
22 scriptedEffect.name = "Grow from seeds" # no tr (don't translate it because modules find effects by name)
23 scriptedEffect.title = _("Grow from seeds")
25 self.minimumNumberOfSegmentsWithEditableArea = 1 # if mask is specified then one input segment is sufficient
26 self.clippedMasterImageDataRequired = True # source volume intensities are used by this effect
27 self.clippedMaskImageDataRequired = True # masking is used
28 self.growCutFilter = None
29
30 def clone(self):
31 import qSlicerSegmentationsEditorEffectsPythonQt as effects
32
33 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(None)
34 clonedEffect.setPythonSource(__file__.replace("\\", "/"))
35 return clonedEffect
36
37 def icon(self):
38 iconPath = os.path.join(os.path.dirname(__file__), "Resources/Icons/GrowFromSeeds.png")
39 if os.path.exists(iconPath):
40 return qt.QIcon(iconPath)
41 return qt.QIcon()
42
43 def helpText(self):
44 return "<html>" + _("""Growing segments to create complete segmentation<br>.
45 Location, size, and shape of initial segments and content of source volume are taken into account.
46 Final segment boundaries will be placed where source volume brightness changes abruptly. Instructions:<p>
47 <ul style="margin: 0">
48 <li>Use Paint or other effects to draw seeds in each region that should belong to a separate segment.
49 Paint each seed with a different segment. Minimum two segments are required.
50 <li>Click <dfn>Initialize</dfn> to compute preview of full segmentation.
51 <li>Browse through image slices. If previewed segmentation result is not correct then switch to
52 Paint or other effects and add more seeds in the misclassified region. Full segmentation will be
53 updated automatically within a few seconds
54 <li>Click <dfn>Apply</dfn> to update segmentation with the previewed result.
55 </ul><p>
56 If segments overlap, segment higher in the segments table will have priority.
57 The effect uses <a href="http://interactivemedical.org/imic2014/CameraReadyPapers/Paper%204/IMIC_ID4_FastGrowCut.pdf">fast grow-cut method</a>.
58 <p>""")
59
60 def reset(self):
61 self.growCutFilter = None
62 AbstractScriptedSegmentEditorAutoCompleteEffect.reset(self)
64
65 def setupOptionsFrame(self):
66 AbstractScriptedSegmentEditorAutoCompleteEffect.setupOptionsFrame(self)
67
68 # Object scale slider
69 self.seedLocalityFactorSlider = slicer.qMRMLSliderWidget()
70 self.seedLocalityFactorSlider.setMRMLScene(slicer.mrmlScene)
71 self.seedLocalityFactorSlider.minimum = 0
72 self.seedLocalityFactorSlider.maximum = 10
73 self.seedLocalityFactorSlider.value = 0.0
74 self.seedLocalityFactorSlider.decimals = 1
75 self.seedLocalityFactorSlider.singleStep = 0.1
76 self.seedLocalityFactorSlider.pageStep = 1.0
77 self.seedLocalityFactorSlider.setToolTip(_('Increasing this value makes the effect of seeds more localized,'
78 ' thereby reducing leaks, but requires seed regions to be more evenly distributed in the image.'
79 ' The value is specified as an additional "intensity level difference" per "unit distance."'))
80 self.scriptedEffect.addLabeledOptionsWidget(_("Seed locality:"), self.seedLocalityFactorSlider)
81 self.seedLocalityFactorSlider.connect("valueChanged(double)", self.updateAlgorithmParameterFromGUI)
82
83 def setMRMLDefaults(self):
84 AbstractScriptedSegmentEditorAutoCompleteEffect.setMRMLDefaults(self)
85 self.scriptedEffect.setParameterDefault("SeedLocalityFactor", 0.0)
86
87 def updateGUIFromMRML(self):
88 AbstractScriptedSegmentEditorAutoCompleteEffect.updateGUIFromMRML(self)
89 if self.scriptedEffect.parameterDefined("SeedLocalityFactor"):
90 seedLocalityFactor = self.scriptedEffect.doubleParameter("SeedLocalityFactor")
91 else:
92 seedLocalityFactor = 0.0
93 wasBlocked = self.seedLocalityFactorSlider.blockSignals(True)
94 self.seedLocalityFactorSlider.value = abs(seedLocalityFactor)
95 self.seedLocalityFactorSlider.blockSignals(wasBlocked)
96
97 def updateMRMLFromGUI(self):
98 AbstractScriptedSegmentEditorAutoCompleteEffect.updateMRMLFromGUI(self)
99 self.scriptedEffect.setParameter("SeedLocalityFactor", self.seedLocalityFactorSlider.value)
100
101 def updateAlgorithmParameterFromGUI(self):
102 self.updateMRMLFromGUI()
103
104 # Trigger preview update
105 if self.getPreviewNode():
106 self.delayedAutoUpdateTimer.start()
107
108 def computePreviewLabelmap(self, mergedImage, outputLabelmap):
109 import vtkITK
110
111 if not self.growCutFilter:
112 self.growCutFilter = vtkITK.vtkITKGrowCut()
113 self.growCutFilter.SetIntensityVolume(self.clippedMasterImageData)
114 self.growCutFilter.SetMaskVolume(self.clippedMaskImageData)
115 maskExtent = self.clippedMaskImageData.GetExtent() if self.clippedMaskImageData else None
116 if (
117 maskExtent is not None
118 and maskExtent[0] <= maskExtent[1]
119 and maskExtent[2] <= maskExtent[3]
120 and maskExtent[4] <= maskExtent[5]
121 ):
122 # Mask is used.
123 # Grow the extent more, as background segment does not surround region of interest.
125 else:
126 # No masking is used.
127 # Background segment is expected to surround region of interest, so narrower margin is enough.
128 self.extentGrowthRatio = 0.20
129
130 if self.scriptedEffect.parameterDefined("SeedLocalityFactor"):
131 seedLocalityFactor = self.scriptedEffect.doubleParameter("SeedLocalityFactor")
132 else:
133 seedLocalityFactor = 0.0
134 self.growCutFilter.SetDistancePenalty(seedLocalityFactor)
135 self.growCutFilter.SetSeedLabelVolume(mergedImage)
136 startTime = time.time()
137 self.growCutFilter.Update()
138 logging.info("Grow-cut operation on volume of {}x{}x{} voxels was completed in {:3.1f} seconds.".format(
139 self.clippedMasterImageData.GetDimensions()[0],
140 self.clippedMasterImageData.GetDimensions()[1],
141 self.clippedMasterImageData.GetDimensions()[2],
142 time.time() - startTime))
143
144 outputLabelmap.DeepCopy(self.growCutFilter.GetOutput())
145 imageToWorld = vtk.vtkMatrix4x4()
146 mergedImage.GetImageToWorldMatrix(imageToWorld)
147 outputLabelmap.SetImageToWorldMatrix(imageToWorld)