Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Make Grow from seeds effect input requirements more clear #7326

Merged
merged 1 commit into from Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -31,7 +31,10 @@ def __init__(self, scriptedEffect):
scriptedEffect.perSegment = False
AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)

# Number of segments required when editable area is not specified
self.minimumNumberOfSegments = 1
# Number of segments required when editable area is specified
self.minimumNumberOfSegmentsWithEditableArea = 1
self.clippedMasterImageDataRequired = False
self.clippedMaskImageDataRequired = False

Expand Down Expand Up @@ -168,8 +171,9 @@ def onSegmentationModified(self, caller, event):
segment = segmentation.GetSegment(segmentID)
if not segment:
# selected segment was deleted, cancel segmentation
logging.debug("Segmentation cancelled because an input segment was deleted")
logging.debug("Segmentation operation is cancelled because an input segment was deleted")
self.onCancel()
slicer.util.showStatusMessage(_("Segmentation operation is cancelled because an input segment was deleted."), 3000)
return
segmentLabelmap = segment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())
if segmentID in self.selectedSegmentModifiedTimes \
Expand Down Expand Up @@ -228,7 +232,9 @@ def observeSegmentation(self, observationEnabled):

def getPreviewNode(self):
previewNode = self.scriptedEffect.parameterSetNode().GetNodeReference(ResultPreviewNodeReferenceRole)
if previewNode and self.scriptedEffect.parameter("SegmentationResultPreviewOwnerEffect") != self.scriptedEffect.name:
if (previewNode
and self.scriptedEffect.parameterDefined("SegmentationResultPreviewOwnerEffect")
and self.scriptedEffect.parameter("SegmentationResultPreviewOwnerEffect") != self.scriptedEffect.name):
# another effect owns this preview node
return None
return previewNode
Expand Down Expand Up @@ -279,13 +285,10 @@ def onPreview(self):

slicer.util.showStatusMessage(_("Running {effectName} auto-complete...").format(effectName=self.scriptedEffect.name), 2000)
try:
# This can be a long operation - indicate it to the user
qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
self.preview()
with slicer.util.tryWithErrorDisplay(_("Segmentation operation failed:"), waitCursor=True):
self.preview()
finally:
qt.QApplication.restoreOverrideCursor()

self.previewComputationInProgress = False
self.previewComputationInProgress = False

def reset(self):
self.delayedAutoUpdateTimer.stop()
Expand Down Expand Up @@ -436,10 +439,28 @@ def preview(self):
if self.selectedSegmentIds is None:
self.selectedSegmentIds = vtk.vtkStringArray()
segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(self.selectedSegmentIds)
if self.selectedSegmentIds.GetNumberOfValues() < self.minimumNumberOfSegments:
logging.error(f"Auto-complete operation skipped: at least {self.minimumNumberOfSegments} visible segments are required")

if self.minimumNumberOfSegments != self.minimumNumberOfSegmentsWithEditableArea:
editableAreaSpecified = (
self.scriptedEffect.parameterSetNode().GetSourceVolumeIntensityMask()
or self.scriptedEffect.parameterSetNode().GetMaskMode() != slicer.vtkMRMLSegmentationNode.EditAllowedEverywhere)
if editableAreaSpecified and self.selectedSegmentIds.GetNumberOfValues() < self.minimumNumberOfSegmentsWithEditableArea:
logging.error(f"Auto-complete operation failed: at least {self.minimumNumberOfSegmentsWithEditableArea} visible segments are required when editable area is defined")
raise RuntimeError(
_("Minimum {minimumNumberOfSegments} visible segments are required.").format(
minimumNumberOfSegments=self.minimumNumberOfSegmentsWithEditableArea))
elif (not editableAreaSpecified) and self.selectedSegmentIds.GetNumberOfValues() < self.minimumNumberOfSegments:
logging.error(f"Auto-complete operation skipped: at least {self.minimumNumberOfSegmentsWithEditableArea} visible segments or setting of editable area is required")
raise RuntimeError(
_("Minimum {minimumNumberOfSegments} visible segments (or specification of editable area or intensity range) is required.").format(
minimumNumberOfSegments=self.minimumNumberOfSegments))
elif self.selectedSegmentIds.GetNumberOfValues() < self.minimumNumberOfSegments:
# Same number of input segments required regardless of editable area
logging.error(f"Auto-complete operation failed: at least {self.minimumNumberOfSegments} visible segments are required")
self.selectedSegmentIds = None
return
raise RuntimeError(
_("Minimum {minimumNumberOfSegments} visible segments are required.").format(
minimumNumberOfSegments=self.minimumNumberOfSegments))

# Compute merged labelmap extent (effective extent slightly expanded)
if not self.mergedLabelmapGeometryImage:
Expand Down
Expand Up @@ -22,6 +22,7 @@ def __init__(self, scriptedEffect):
scriptedEffect.name = 'Grow from seeds' # no tr (don't translate it because modules find effects by name)
scriptedEffect.title = _('Grow from seeds')
self.minimumNumberOfSegments = 2
self.minimumNumberOfSegmentsWithEditableArea = 1 # if mask is specified then one input segment is sufficient
self.clippedMasterImageDataRequired = True # source volume intensities are used by this effect
self.clippedMaskImageDataRequired = True # masking is used
self.growCutFilter = None
Expand Down