Slicer学习笔记(二十三)slicer调试自带模块
关于slicer的调试设置请参考我的第二十篇博客:Slicer学习笔记(二十)slicer编写自己的扩展模块
1、调试Segment Editor模块下的Grow from seeds
这次笔记是调试 Segment Editor模块下的Grow from seeds 效应。
执行initialize后的打印信息:
self.extentGrowthRatio = 0.1
masterImageExtent = (0, 127, 0, 127, 0, 255)
labelsEffectiveExtent = (4, 117, 11, 119, 90, 255)
labelsExpandedExtent = [0, 127, 1, 127, 74, 255]
2、调试文件与细节
输入图像信息
被调试文件
C:\ProgramData\NA-MIC\Slicer 4.11.20210226\lib\Slicer-4.11\qt-scripted-modules\SegmentEditorEffects\AbstractScriptedSegmentEditorAutoCompleteEffect.py
根据调试信息可以确定对象与变量的对应关系:
masterImageData = self.scriptedEffect.masterVolumeImageData()
2.1、masterImageData的调试信息如下,可以确定该变量对应input_volume.
vtkOrientedImageData (000001B79F65CEE0)
Debug: Off
Modified Time: 101167526
Reference Count: 2
Registered Events: (none)
Information: 000001B79AE0CE80
Data Released: False
Global Release Data: Off
UpdateTime: 56141501
Field Data:
Debug: Off
Modified Time: 101167524
Reference Count: 1
Registered Events: (none)
Number Of Arrays: 0
Number Of Components: 0
Number Of Tuples: 0
Number Of Points: 4194304
Number Of Cells: 4112895
Cell Data:
Debug: Off
Modified Time: 101167499
Reference Count: 1
Registered Events:
Registered Observers:
vtkObserver (000001B79A31ECE0)
Event: 36
EventName: ModifiedEvent
Command: 000001B79AE0D830
Priority: 0
Tag: 1
Number Of Arrays: 0
Number Of Components: 0
Number Of Tuples: 0
Copy Tuple Flags: ( 1 1 1 1 1 0 1 1 )
Interpolate Flags: ( 1 1 1 1 1 0 0 1 )
Pass Through Flags: ( 1 1 1 1 1 1 1 1 )
Scalars: (none)
Vectors: (none)
Normals: (none)
TCoords: (none)
Tensors: (none)
GlobalIds: (none)
PedigreeIds: (none)
EdgeFlag: (none)
Point Data:
Debug: Off
Modified Time: 101167523
Reference Count: 1
Registered Events:
Registered Observers:
vtkObserver (000001B79A31ECB0)
Event: 36
EventName: ModifiedEvent
Command: 000001B79AE0D830
Priority: 0
Tag: 1
Number Of Arrays: 1
Array 0 name = ImageScalars
Number Of Components: 1
Number Of Tuples: 4194304
Copy Tuple Flags: ( 1 1 1 1 1 0 1 1 )
Interpolate Flags: ( 1 1 1 1 1 0 0 1 )
Pass Through Flags: ( 1 1 1 1 1 1 1 1 )
Scalars:
Debug: Off
Modified Time: 101167522
Reference Count: 1
Registered Events: (none)
Name: ImageScalars
Data type: int
Size: 4194304
MaxId: 4194303
NumberOfComponents: 1
Information: 000001B7CFE24A20
Debug: Off
Modified Time: 101167548
Reference Count: 1
Registered Events: (none)
PER_COMPONENT: vtkInformationVector(000001B7CFE22810)
Name: ImageScalars
Number Of Components: 1
Number Of Tuples: 4194304
Size: 4194304
MaxId: 4194303
LookupTable: (none)
Vectors: (none)
Normals: (none)
TCoords: (none)
Tensors: (none)
GlobalIds: (none)
PedigreeIds: (none)
EdgeFlag: (none)
Bounds:
Xmin,Xmax: (27, 155)
Ymin,Ymax: (-97, 31)
Zmin,Zmax: (-732.5, -476.5)
Compute Time: 137081218
Spacing: (1, 1, 1)
Origin: (154.5, 30.5, -732)
Dimensions: (128, 128, 256)
Increments: (0, 0, 0)
Extent: (0, 127, 0, 127, 0, 255)
Directions:
-1 0 0
0 -1 0
0 0 1
2.2、masterImageExtent变量
masterImageExtent = masterImageData.GetExtent()
masterImageExtent变量值:
Extent: (0, 127, 0, 127, 0, 255)
2.3、mergedLabelmapGeometryImage 对应的调试信息
self.mergedLabelmapGeometryImage = slicer.vtkOrientedImageData()
mergedLabelmapGeometryImage 对应的调试信息:
vtkOrientedImageData (000001B7C3AF9DB0)
Bounds:
Xmin,Xmax: (29, 155)
Ymin,Ymax: (-75, 24)
Zmin,Zmax: (-640.5, -487.5)
Compute Time: 137081230
Spacing: (1, 1, 1)
Origin: (154.5, 30.5, -732)
Dimensions: (126, 99, 153)
Increments: (0, 0, 0)
Extent: (0, 125, 7, 105, 92, 244)
Directions:
-1 0 0
0 -1 0
0 0 1
2.4、labelsEffectiveExtent调试信息
labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent()
labelsEffectiveExtent调试信息:
Extent: (0, 125, 7, 105, 92, 244)
2.5、打印输出的变量
masterImageData = self.scriptedEffect.masterVolumeImageData()
masterImageExtent = masterImageData.GetExtent()
labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent()
# Margin size is relative to combined seed region size, but minimum of 3 voxels
print("self.extentGrowthRatio = {0}".format(self.extentGrowthRatio))
margin = [
int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[1]-labelsEffectiveExtent[0]))),
int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[3]-labelsEffectiveExtent[2]))),
int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[5]-labelsEffectiveExtent[4]))) ]
labelsExpandedExtent = [
max(masterImageExtent[0], labelsEffectiveExtent[0]-margin[0]),
min(masterImageExtent[1], labelsEffectiveExtent[1]+margin[0]),
max(masterImageExtent[2], labelsEffectiveExtent[2]-margin[1]),
min(masterImageExtent[3], labelsEffectiveExtent[3]+margin[1]),
max(masterImageExtent[4], labelsEffectiveExtent[4]-margin[2]),
min(masterImageExtent[5], labelsEffectiveExtent[5]+margin[2]) ]
print("masterImageExtent = "+repr(masterImageExtent))
print("labelsEffectiveExtent = "+repr(labelsEffectiveExtent))
print("labelsExpandedExtent = "+repr(labelsExpandedExtent))
2.6、mergedImage 信息
mergedImage = slicer.vtkOrientedImageData()
segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds)
调试信息
Bounds:
Xmin,Xmax: (27, 155)
Ymin,Ymax: (-97, 31)
Zmin,Zmax: (-716.5, -476.5)
Compute Time: 137093904
Spacing: (1, 1, 1)
Origin: (154.5, 30.5, -732)
Dimensions: (128, 128, 240)
Increments: (0, 0, 0)
Extent: (0, 127, 0, 127, 16, 255)
Directions:
-1 0 0
0 -1 0
0 0 1
2.7、self.clippedMasterImageData 信息
if not self.growCutFilter:
self.growCutFilter = vtkSlicerSegmentationsModuleLogic.vtkImageGrowCutSegment()
self.growCutFilter.SetIntensityVolume(self.clippedMasterImageData)
self.growCutFilter.SetMaskVolume(self.clippedMaskImageData)
调试信息
Bounds:
Xmin,Xmax: (27, 155)
Ymin,Ymax: (-97, 31)
Zmin,Zmax: (-716.5, -476.5)
Compute Time: 137088616
Spacing: (1, 1, 1)
Origin: (154.5, 30.5, -732)
Dimensions: (128, 128, 240)
Increments: (0, 0, 0)
Extent: (0, 127, 0, 127, 16, 255)
Directions:
-1 0 0
0 -1 0
0 0 1
3、Apply 按钮
C:\ProgramData\NA-MIC\Slicer 4.11.20210226\lib\Slicer-4.11\qt-scripted-modules\SegmentEditorEffects\AbstractScriptedSegmentEditorAutoCompleteEffect.py
Apply 按钮只是起一个生效的作用,实际操作是在 Initialize这个按钮完成的,也就是第2部分“调试文件与细节”中完成的。
4、主要计算与显示
4.1、主要计算
def computePreviewLabelmap(self, mergedImage, outputLabelmap):
import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
if not self.growCutFilter:
self.growCutFilter = vtkSlicerSegmentationsModuleLogic.vtkImageGrowCutSegment()
self.growCutFilter.SetIntensityVolume(self.clippedMasterImageData)
self.growCutFilter.SetMaskVolume(self.clippedMaskImageData)
maskExtent = self.clippedMaskImageData.GetExtent() if self.clippedMaskImageData else None
if maskExtent is not None and maskExtent[0] <= maskExtent[1] and maskExtent[2] <= maskExtent[3] and maskExtent[4] <= maskExtent[5]:
# Mask is used.
# Grow the extent more, as background segment does not surround region of interest.
self.extentGrowthRatio = 0.50
else:
# No masking is used.
# Background segment is expected to surround region of interest, so narrower margin is enough.
self.extentGrowthRatio = 0.20
if self.scriptedEffect.parameterDefined("SeedLocalityFactor"):
seedLocalityFactor = self.scriptedEffect.doubleParameter("SeedLocalityFactor")
else:
seedLocalityFactor = 0.0
self.growCutFilter.SetDistancePenalty(seedLocalityFactor)
self.growCutFilter.SetSeedLabelVolume(mergedImage)
startTime = time.time()
self.growCutFilter.Update()
logging.info('Grow-cut operation on volume of {0}x{1}x{2} voxels was completed in {3:3.1f} seconds.'.format(
self.clippedMasterImageData.GetDimensions()[0],
self.clippedMasterImageData.GetDimensions()[1],
self.clippedMasterImageData.GetDimensions()[2],
time.time() - startTime))
outputLabelmap.DeepCopy( self.growCutFilter.GetOutput() )
4.2、Initialize和Applay
4.2.1 Initialize
def preview(self):
# Get master volume image data
import vtkSegmentationCorePython as vtkSegmentationCore
# Get segmentation
segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
previewNode = self.getPreviewNode()
previewOpacity = self.getPreviewOpacity()
previewShow3D = self.getPreviewShow3D()
# If the selectedSegmentIds have been specified, then they shouldn't be overwritten here
currentSelectedSegmentIds = self.selectedSegmentIds
if self.effectiveExtentChanged():
self.reset()
# Restore the selectedSegmentIds
self.selectedSegmentIds = currentSelectedSegmentIds
if self.selectedSegmentIds is None:
self.selectedSegmentIds = vtk.vtkStringArray()
segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(self.selectedSegmentIds)
if self.selectedSegmentIds.GetNumberOfValues() < self.minimumNumberOfSegments:
logging.error("Auto-complete operation skipped: at least {0} visible segments are required".format(self.minimumNumberOfSegments))
self.selectedSegmentIds = None
return
# Compute merged labelmap extent (effective extent slightly expanded)
if not self.mergedLabelmapGeometryImage:
self.mergedLabelmapGeometryImage = slicer.vtkOrientedImageData()
commonGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.selectedSegmentIds)
if not commonGeometryString:
logging.info("Auto-complete operation skipped: all visible segments are empty")
return
vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(commonGeometryString, self.mergedLabelmapGeometryImage)
masterImageData = self.scriptedEffect.masterVolumeImageData()
masterImageExtent = masterImageData.GetExtent()
labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent()
# Margin size is relative to combined seed region size, but minimum of 3 voxels
print("self.extentGrowthRatio = {0}".format(self.extentGrowthRatio))
margin = [
int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[1]-labelsEffectiveExtent[0]))),
int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[3]-labelsEffectiveExtent[2]))),
int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[5]-labelsEffectiveExtent[4]))) ]
labelsExpandedExtent = [
max(masterImageExtent[0], labelsEffectiveExtent[0]-margin[0]),
min(masterImageExtent[1], labelsEffectiveExtent[1]+margin[0]),
max(masterImageExtent[2], labelsEffectiveExtent[2]-margin[1]),
min(masterImageExtent[3], labelsEffectiveExtent[3]+margin[1]),
max(masterImageExtent[4], labelsEffectiveExtent[4]-margin[2]),
min(masterImageExtent[5], labelsEffectiveExtent[5]+margin[2]) ]
print("masterImageExtent = "+repr(masterImageExtent))
print("labelsEffectiveExtent = "+repr(labelsEffectiveExtent))
print("labelsExpandedExtent = "+repr(labelsExpandedExtent))
self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent)
# Create and setup preview node
previewNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
previewNode.CreateDefaultDisplayNodes()
previewNode.GetDisplayNode().SetVisibility2DOutline(False)
if segmentationNode.GetParentTransformNode():
previewNode.SetAndObserveTransformNodeID(segmentationNode.GetParentTransformNode().GetID())
self.scriptedEffect.parameterSetNode().SetNodeReferenceID(ResultPreviewNodeReferenceRole, previewNode.GetID())
self.scriptedEffect.setCommonParameter("SegmentationResultPreviewOwnerEffect", self.scriptedEffect.name)
self.setPreviewOpacity(0.6)
# Disable smoothing for closed surface generation to make it fast
previewNode.GetSegmentation().SetConversionParameter(
slicer.vtkBinaryLabelmapToClosedSurfaceConversionRule.GetSmoothingFactorParameterName(),
"-0.5");
inputContainsClosedSurfaceRepresentation = segmentationNode.GetSegmentation().ContainsRepresentation(
slicer.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
self.setPreviewShow3D(inputContainsClosedSurfaceRepresentation)
if self.clippedMasterImageDataRequired:
self.clippedMasterImageData = slicer.vtkOrientedImageData()
masterImageClipper = vtk.vtkImageConstantPad()
masterImageClipper.SetInputData(masterImageData)
masterImageClipper.SetOutputWholeExtent(self.mergedLabelmapGeometryImage.GetExtent())
masterImageClipper.Update()
self.clippedMasterImageData.ShallowCopy(masterImageClipper.GetOutput())
self.clippedMasterImageData.CopyDirections(self.mergedLabelmapGeometryImage)
self.clippedMaskImageData = None
if self.clippedMaskImageDataRequired:
self.clippedMaskImageData = slicer.vtkOrientedImageData()
intensityBasedMasking = self.scriptedEffect.parameterSetNode().GetMasterVolumeIntensityMask()
# add wmz
mode = self.scriptedEffect.parameterSetNode().GetMaskMode()
maskSegmentID = self.scriptedEffect.parameterSetNode().GetMaskSegmentID() if self.scriptedEffect.parameterSetNode().GetMaskSegmentID() else ""
masterVolume = self.clippedMasterImageData if intensityBasedMasking else None
editableIntensityRange = self.scriptedEffect.parameterSetNode().GetMasterVolumeIntensityMaskRange() if intensityBasedMasking else None
# add wmz
success = segmentationNode.GenerateEditMask(self.clippedMaskImageData,
self.scriptedEffect.parameterSetNode().GetMaskMode(),
self.clippedMasterImageData, # reference geometry
"", # edited segment ID
self.scriptedEffect.parameterSetNode().GetMaskSegmentID() if self.scriptedEffect.parameterSetNode().GetMaskSegmentID() else "",
self.clippedMasterImageData if intensityBasedMasking else None,
self.scriptedEffect.parameterSetNode().GetMasterVolumeIntensityMaskRange() if intensityBasedMasking else None)
if not success:
logging.error("Failed to create edit mask")
self.clippedMaskImageData = None
maskExtent = self.clippedMaskImageData.GetExtent() if self.clippedMaskImageData else None # add wmz
previewNode.SetName(segmentationNode.GetName()+" preview")
mergedImage = slicer.vtkOrientedImageData()
segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds)
outputLabelmap = slicer.vtkOrientedImageData()
self.computePreviewLabelmap(mergedImage, outputLabelmap)
# Write output segmentation results in segments
for index in range(self.selectedSegmentIds.GetNumberOfValues()):
segmentID = self.selectedSegmentIds.GetValue(index)
segment = segmentationNode.GetSegmentation().GetSegment(segmentID)
# Disable save with scene?
# Get only the label of the current segment from the output image
thresh = vtk.vtkImageThreshold()
thresh.ReplaceInOn()
thresh.ReplaceOutOn()
thresh.SetInValue(1)
thresh.SetOutValue(0)
labelValue = index + 1 # n-th segment label value = n + 1 (background label value is 0)
thresh.ThresholdBetween(labelValue, labelValue);
thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
thresh.SetInputData(outputLabelmap)
thresh.Update()
# Write label to segment
newSegmentLabelmap = slicer.vtkOrientedImageData()
newSegmentLabelmap.ShallowCopy(thresh.GetOutput())
newSegmentLabelmap.CopyDirections(mergedImage)
newSegment = previewNode.GetSegmentation().GetSegment(segmentID)
if not newSegment:
newSegment = vtkSegmentationCore.vtkSegment()
newSegment.SetName(segment.GetName())
color = segmentationNode.GetSegmentation().GetSegment(segmentID).GetColor()
newSegment.SetColor(color)
previewNode.GetSegmentation().AddSegment(newSegment, segmentID)
self.scriptedEffect.modifySegmentByLabelmap(previewNode, segmentID, newSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
# Automatically hide result segments that are background (all eight corners are non-zero)
previewNode.GetDisplayNode().SetSegmentVisibility3D(segmentID, not self.isBackgroundLabelmap(newSegmentLabelmap))
# If the preview was reset, we need to restore the visibility options
self.setPreviewOpacity(previewOpacity)
self.setPreviewShow3D(previewShow3D)
self.updateGUIFromMRML()
ResultPreviewNodeReferenceRole = "SegmentationResultPreview"
4.2.2、Apply
def onApply(self):
self.delayedAutoUpdateTimer.stop()
self.observeSegmentation(False)
import vtkSegmentationCorePython as vtkSegmentationCore
segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
segmentationDisplayNode = segmentationNode.GetDisplayNode()
previewNode = self.getPreviewNode()
self.scriptedEffect.saveStateForUndo()
previewContainsClosedSurfaceRepresentation = previewNode.GetSegmentation().ContainsRepresentation(
slicer.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
# Move segments from preview into current segmentation
segmentIDs = vtk.vtkStringArray()
previewNode.GetSegmentation().GetSegmentIDs(segmentIDs)
for index in range(segmentIDs.GetNumberOfValues()):
segmentID = segmentIDs.GetValue(index)
previewSegmentLabelmap = slicer.vtkOrientedImageData()
previewNode.GetBinaryLabelmapRepresentation(segmentID, previewSegmentLabelmap)
self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentID, previewSegmentLabelmap,
slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
if segmentationDisplayNode is not None and self.isBackgroundLabelmap(previewSegmentLabelmap):
# Automatically hide result segments that are background (all eight corners are non-zero)
segmentationDisplayNode.SetSegmentVisibility(segmentID, False)
previewNode.GetSegmentation().RemoveSegment(segmentID) # delete now to limit memory usage
if previewContainsClosedSurfaceRepresentation:
segmentationNode.CreateClosedSurfaceRepresentation()
self.reset()