def resetIslandSegments(self, islandSizes): self.segmentation.RemoveAllSegments() totalSize = 0 voxelSizeSum = 0 for size in islandSizes: totalSize += size + 1 voxelSizeSum += size mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() mergedLabelmapExtent = [0, totalSize-1, 0, 0, 0, 0] self.setupIslandLabelmap(mergedLabelmap, mergedLabelmapExtent, 0) emptySegment = slicer.vtkSegment() emptySegment.SetName("Segment_1") emptySegment.AddRepresentation(self.binaryLabelmapReprName, mergedLabelmap) self.segmentation.AddSegment(emptySegment) self.segmentEditorNode.SetSelectedSegmentID("Segment_1") startExtent = 0 for size in islandSizes: islandLabelmap = vtkSegmentationCore.vtkOrientedImageData() islandExtent = [startExtent, startExtent+size-1, 0, 0, 0, 0] self.setupIslandLabelmap(islandLabelmap, islandExtent) self.paintEffect.modifySelectedSegmentByLabelmap(islandLabelmap, self.paintEffect.ModificationModeAdd) startExtent += size + 1 self.checkSegmentVoxelCount(0, voxelSizeSum) layerCount = self.segmentation.GetNumberOfLayers() self.assertEqual(layerCount, 1)
def TestSection_TestSharedLabelmapMultipleLayerEditing(self): self.segmentation.RemoveAllSegments() self.segmentation.AddEmptySegment("Segment_1") self.segmentation.AddEmptySegment("Segment_2") mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() mergedLabelmap.SetExtent(0, 10, 0, 10, 0, 10) mergedLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) threshold = vtk.vtkImageThreshold() threshold.SetInputData(mergedLabelmap) threshold.ThresholdBetween(vtk.VTK_UNSIGNED_CHAR_MIN, vtk.VTK_UNSIGNED_CHAR_MAX) threshold.SetInValue(1) threshold.SetOutValue(0) threshold.Update() mergedLabelmap.ShallowCopy(threshold.GetOutput()) self.segmentEditorNode.SetSelectedSegmentID("Segment_1") self.paintEffect.modifySelectedSegmentByLabelmap(mergedLabelmap, self.paintEffect.ModificationModeAdd) self.segmentEditorNode.SetSelectedSegmentID("Segment_2") self.paintEffect.modifySelectedSegmentByLabelmap(mergedLabelmap, self.paintEffect.ModificationModeAdd) layerCount = self.segmentation.GetNumberOfLayers() self.assertEqual(layerCount, 1) self.segmentEditorNode.SetOverwriteMode(self.segmentEditorNode.OverwriteNone) self.segmentEditorNode.SetSelectedSegmentID("Segment_1") self.paintEffect.modifySelectedSegmentByLabelmap(mergedLabelmap, self.paintEffect.ModificationModeAdd) layerCount = self.segmentation.GetNumberOfLayers() self.assertEqual(layerCount, 2) logging.info('Multiple layer editing successful')
def onApply(self): try: # Get master volume image data import vtkSegmentationCore masterImageData = vtkSegmentationCore.vtkOrientedImageData() self.scriptedEffect.masterVolumeImageData(masterImageData) # Get edited labelmap editedLabelmap = self.scriptedEffect.parameterSetNode().GetEditedLabelmap() # Get parameters min = self.scriptedEffect.doubleParameter("MinimumThreshold") max = self.scriptedEffect.doubleParameter("MaximumThreshold") # Save state for undo #TODO: #self.undoRedo.saveState() # Perform thresholding thresh = vtk.vtkImageThreshold() thresh.SetInputData(masterImageData) thresh.ThresholdBetween(min, max) thresh.SetInValue(1) thresh.SetOutValue(0) thresh.SetOutputScalarType(editedLabelmap.GetScalarType()) thresh.Update() editedLabelmap.DeepCopy(thresh.GetOutput()) except IndexError: logging.error('apply: Failed to threshold master volume!') pass # Notify editor about changes. # This needs to be called so that the changes are written back to the edited segment self.scriptedEffect.apply() # De-select effect self.scriptedEffect.selectEffect("")
def TestSection_SharedLabelmapMultipleLayerEditing(self): self.segmentation.RemoveAllSegments() self.segmentation.AddEmptySegment("Segment_1") self.segmentation.AddEmptySegment("Segment_2") mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() mergedLabelmap.SetExtent(0, 10, 0, 10, 0, 10) mergedLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) mergedLabelmap.GetPointData().GetScalars().Fill(1) oldOverwriteMode = self.segmentEditorNode.GetOverwriteMode() self.segmentEditorNode.SetOverwriteMode( self.segmentEditorNode.OverwriteAllSegments) self.segmentEditorNode.SetSelectedSegmentID("Segment_1") self.paintEffect.modifySelectedSegmentByLabelmap( mergedLabelmap, self.paintEffect.ModificationModeAdd) self.segmentEditorNode.SetSelectedSegmentID("Segment_2") self.paintEffect.modifySelectedSegmentByLabelmap( mergedLabelmap, self.paintEffect.ModificationModeAdd) layerCount = self.segmentation.GetNumberOfLayers() self.assertEqual(layerCount, 1) self.segmentEditorNode.SetOverwriteMode( self.segmentEditorNode.OverwriteNone) self.segmentEditorNode.SetSelectedSegmentID("Segment_1") self.paintEffect.modifySelectedSegmentByLabelmap( mergedLabelmap, self.paintEffect.ModificationModeAdd) layerCount = self.segmentation.GetNumberOfLayers() self.assertEqual(layerCount, 2) self.segmentEditorNode.SetOverwriteMode(oldOverwriteMode) logging.info('Multiple layer editing successful')
def onApply(self): #TODO: #self.logic.undoRedo = self.undoRedo # Get parameters fullyConnected = self.scriptedEffect.integerParameter("FullyConnected") minimumSize = self.scriptedEffect.integerParameter("MinimumSize") # Get edited labelmap editedLabelmap = self.scriptedEffect.parameterSetNode().GetEditedLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(editedLabelmap) castIn.SetOutputScalarTypeToUnsignedLong() # Identify the islands in the inverted volume and # find the pixel that corresponds to the background islandMath = vtkITK.vtkITKIslandMath() islandMath.SetInputConnection(castIn.GetOutputPort()) islandMath.SetFullyConnected(fullyConnected) islandMath.SetMinimumSize(minimumSize) # Note that island operation happens in unsigned long space # but the segment editor works in unsigned char castOut = vtk.vtkImageCast() castOut.SetInputConnection(islandMath.GetOutputPort()) castOut.SetOutputScalarTypeToUnsignedChar() castOut.Update() islandCount = islandMath.GetNumberOfIslands() islandOrigCount = islandMath.GetOriginalNumberOfIslands() ignoredIslands = islandOrigCount - islandCount logging.info( "%d islands created (%d ignored)" % (islandCount, ignoredIslands) ) # Create oriented image data from output import vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(castOut.GetOutput()) editedLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() editedLabelmap.GetImageToWorldMatrix(editedLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix(editedLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation import vtkSlicerSegmentationsModuleMRML segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName() import vtkSlicerSegmentationsModuleLogic vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \ multiLabelImage, segmentationNode, selectedSegmentName ) # Set labelmap visibility to outline for the new segments displayNode = segmentationNode.GetDisplayNode() for index in xrange(1,islandCount+1): segmentID = selectedSegmentName + "_" + str(index) displayNode.SetSegmentVisibility2DFill(segmentID, False) displayNode.SetSegmentVisibility2DOutline(segmentID, True)
def masterVolumeNodeChanged(self): # Set scalar range of master volume image data to threshold slider import vtkSegmentationCore masterImageData = vtkSegmentationCore.vtkOrientedImageData() success = self.scriptedEffect.masterVolumeImageData(masterImageData) if success: lo, hi = masterImageData.GetScalarRange() self.thresholdSlider.minimum, self.thresholdSlider.maximum = lo, hi self.thresholdSlider.singleStep = (hi - lo) / 1000.
def TestSection_MergeLabelmapWithDifferentGeometries(self): # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions) logging.info('Test section: Merge labelmap with different geometries') self.assertIsNotNone(self.sphereSegment) self.sphereSegment.RemoveRepresentation(self.binaryLabelmapReprName) self.assertIsNone(self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName)) # Create new segmentation with sphere segment self.secondSegmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode', 'Second') self.secondSegmentationNode.GetSegmentation().SetMasterRepresentationName(self.binaryLabelmapReprName) self.secondSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment) # Check automatically converted labelmap. It is supposed to have the default geometry # (which is different than the one in the input segmentation) sphereLabelmap = self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName) self.assertIsNotNone(sphereLabelmap) sphereLabelmapSpacing = sphereLabelmap.GetSpacing() self.assertAlmostEqual(sphereLabelmapSpacing[0], 0.629257364931788, 8) self.assertAlmostEqual(sphereLabelmapSpacing[1], 0.629257364931788, 8) self.assertAlmostEqual(sphereLabelmapSpacing[2], 0.629257364931788, 8) # Create binary labelmap in segmentation that will create the merged labelmap from # different geometries so that labelmap is not removed from sphere segment when adding self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName) # Copy segment to input segmentation self.inputSegmentationNode.GetSegmentation().CopySegmentFromSegmentation(self.secondSegmentationNode.GetSegmentation(), self.sphereSegmentName) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3) # Check merged labelmap # Reference geometry has the tiny patient spacing, and it is oversampled to have similar # voxel size as the sphere labelmap with the uniform 0.629mm spacing mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0) mergedLabelmapSpacing = mergedLabelmap.GetSpacing() self.assertAlmostEqual(mergedLabelmapSpacing[0], 0.80327868852459, 8) self.assertAlmostEqual(mergedLabelmapSpacing[1], 0.80327868852459, 8) self.assertAlmostEqual(mergedLabelmapSpacing[2], 0.377049180327869, 8) imageStat = vtk.vtkImageAccumulate() imageStat.SetInputData(mergedLabelmap) imageStat.SetComponentExtent(0,5,0,0,0,0) imageStat.SetComponentOrigin(0,0,0) imageStat.SetComponentSpacing(1,1,1) imageStat.Update() imageStatResult = imageStat.GetOutput() for i in range(5): logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0))) self.assertEqual(imageStat.GetVoxelCount(), 226981000) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 178838889) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 39705288) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 890883) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 7545940) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(4,0,0,0), 0) # Built from color table and color four is removed in previous test section
def TestSection_MergeLabelmapWithDifferentGeometries(self): # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions) logging.info('Test section: Merge labelmap with different geometries') self.assertIsNotNone(self.sphereSegment) self.sphereSegment.RemoveRepresentation(self.binaryLabelmapReprName) self.assertIsNone(self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName)) # Create new segmentation with sphere segment self.secondSegmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode', 'Second') self.secondSegmentationNode.GetSegmentation().SetMasterRepresentationName(self.binaryLabelmapReprName) self.secondSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment) # Check automatically converted labelmap. It is supposed to have the default geometry # (which is different than the one in the input segmentation) sphereLabelmap = self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName) self.assertIsNotNone(sphereLabelmap) sphereLabelmapSpacing = sphereLabelmap.GetSpacing() self.assertAlmostEqual(sphereLabelmapSpacing[0], 0.629257364931788, 8) self.assertAlmostEqual(sphereLabelmapSpacing[1], 0.629257364931788, 8) self.assertAlmostEqual(sphereLabelmapSpacing[2], 0.629257364931788, 8) # Create binary labelmap in segmentation that will create the merged labelmap from # different geometries so that labelmap is not removed from sphere segment when adding self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName) # Copy segment to input segmentation self.inputSegmentationNode.GetSegmentation().CopySegmentFromSegmentation(self.secondSegmentationNode.GetSegmentation(), self.sphereSegmentName) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3) # Check merged labelmap # Reference geometry has the tiny patient spacing, and it is oversampled to have similar # voxel size as the sphere labelmap with the uniform 0.629mm spacing mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0) mergedLabelmapSpacing = mergedLabelmap.GetSpacing() self.assertAlmostEqual(mergedLabelmapSpacing[0], 0.80327868852459, 8) self.assertAlmostEqual(mergedLabelmapSpacing[1], 0.80327868852459, 8) self.assertAlmostEqual(mergedLabelmapSpacing[2], 0.377049180327869, 8) imageStat = vtk.vtkImageAccumulate() imageStat.SetInputData(mergedLabelmap) imageStat.SetComponentExtent(0, 5, 0, 0, 0, 0) imageStat.SetComponentOrigin(0, 0, 0) imageStat.SetComponentSpacing(1, 1, 1) imageStat.Update() imageStatResult = imageStat.GetOutput() for i in range(5): logging.info(f"Volume {i}: {imageStatResult.GetScalarComponentAsDouble(i,0,0,0)}") self.assertEqual(imageStat.GetVoxelCount(), 226981000) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 178838889) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 39705288) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 890883) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 7545940) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(4, 0, 0, 0), 0) # Built from color table and color four is removed in previous test section
def preview(self,xy): # Calculate the current level trace view if the mouse is inside the volume extent # Get master volume image data import vtkSegmentationCore masterImageData = vtkSegmentationCore.vtkOrientedImageData() self.effect.scriptedEffect.masterVolumeImageData(masterImageData) self.xyPoints.Reset() ijk = self.effect.xyToIjk(xy, self.sliceWidget, masterImageData) dimensions = masterImageData.GetDimensions() for index in xrange(3): # TracingFilter crashes if it receives a seed point at the edge of the image, # so only accept the point if it is inside the image and is at least one pixel away from the edge if ijk[index] < 1 or ijk[index] >= dimensions[index]-1: return self.tracingFilter.SetInputData(masterImageData) self.tracingFilter.SetSeed(ijk) # Select the plane corresponding to current slice orientation # for the input volume sliceNode = self.effect.scriptedEffect.viewNode(self.sliceWidget) offset = max(sliceNode.GetDimensions()) i0,j0,k0 = self.effect.xyToIjk((0,0), self.sliceWidget, masterImageData) i1,j1,k1 = self.effect.xyToIjk((offset,offset), self.sliceWidget, masterImageData) if i0 == i1: self.tracingFilter.SetPlaneToJK() if j0 == j1: self.tracingFilter.SetPlaneToIK() if k0 == k1: self.tracingFilter.SetPlaneToIJ() self.tracingFilter.Update() polyData = self.tracingFilter.GetOutput() # Get master volume IJK to slice XY transform xyToRas = sliceNode.GetXYToRAS() rasToIjk = vtk.vtkMatrix4x4() masterImageData.GetImageToWorldMatrix(rasToIjk) rasToIjk.Invert() xyToIjk = vtk.vtkGeneralTransform() xyToIjk.PostMultiply() xyToIjk.Concatenate(xyToRas) xyToIjk.Concatenate(rasToIjk) ijkToXy = xyToIjk.GetInverse() ijkToXy.TransformPoints(polyData.GetPoints(), self.xyPoints) self.polyData.DeepCopy(polyData) self.polyData.GetPoints().DeepCopy(self.xyPoints) self.sliceWidget.sliceView().scheduleRender()
def onApply(self): # Get master volume image data import vtkSegmentationCore masterImageData = vtkSegmentationCore.vtkOrientedImageData() self.scriptedEffect.masterVolumeImageData(masterImageData) # Check validity of master volume: whether scalar type is supported if masterImageData.GetScalarType() != vtk.VTK_SHORT: logging.warning("GrowCut is attempted with image type '{0}', which is not directly supported".format(masterImageData.GetScalarTypeAsString())) if not slicer.util.confirmOkCancelDisplay("Current image type is '{0}', which is not supported by GrowCut. " "The image scalars will be temporarily casted to short.\n\nIf the segmentation result is not satisfacory, " "then it may mean the scalar range of the master image is out of the range covered by short." .format(masterImageData.GetScalarTypeAsString()), windowTitle='Segment editor'): logging.warning('GrowCut is cancelled by the user') return slicer.util.showStatusMessage("Running GrowCut...", 2000) #TODO: #self.logic.undoRedo = self.undoRedo self.growCut() slicer.util.showStatusMessage("GrowCut Finished", 2000)
def apply(self, segmentMarkupNode, segmentModel, text, textDepth, mode): self.updateModel(segmentMarkupNode, segmentModel, text, textDepth) import vtkSegmentationCore if not segmentMarkupNode: raise AttributeError(f"{self.__class__.__name__}: segment markup node not set.") if not segmentModel: raise AttributeError(f"{self.__class__.__name__}: segment model not set.") if segmentMarkupNode and segmentModel.GetPolyData().GetNumberOfCells() > 0: segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() if not segmentationNode: raise AttributeError(f"{self.__class__.__name__}: Segmentation node not set.") modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() if not modifierLabelmap: raise AttributeError("{}: ModifierLabelmap not set. This can happen for various reasons:\n" "No master volume set for segmentation,\n" "No existing segments for segmentation, or\n" "No referenceImageGeometry is specified in the segmentation".format(self.__class__.__name__)) WorldToModifierLabelmapIjkTransform = vtk.vtkTransform() WorldToModifierLabelmapIjkTransformer = vtk.vtkTransformPolyDataFilter() WorldToModifierLabelmapIjkTransformer.SetTransform(WorldToModifierLabelmapIjkTransform) WorldToModifierLabelmapIjkTransformer.SetInputConnection(segmentModel.GetPolyDataConnection()) segmentationToSegmentationIjkTransformMatrix = vtk.vtkMatrix4x4() modifierLabelmap.GetImageToWorldMatrix(segmentationToSegmentationIjkTransformMatrix) segmentationToSegmentationIjkTransformMatrix.Invert() WorldToModifierLabelmapIjkTransform.Concatenate(segmentationToSegmentationIjkTransformMatrix) worldToSegmentationTransformMatrix = vtk.vtkMatrix4x4() slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes(None, segmentationNode.GetParentTransformNode(), worldToSegmentationTransformMatrix) WorldToModifierLabelmapIjkTransform.Concatenate(worldToSegmentationTransformMatrix) WorldToModifierLabelmapIjkTransformer.Update() polyToStencil = vtk.vtkPolyDataToImageStencil() polyToStencil.SetOutputSpacing(1.0, 1.0, 1.0) polyToStencil.SetInputConnection(WorldToModifierLabelmapIjkTransformer.GetOutputPort()) boundsIjk = WorldToModifierLabelmapIjkTransformer.GetOutput().GetBounds() modifierLabelmapExtent = self.scriptedEffect.modifierLabelmap().GetExtent() polyToStencil.SetOutputWholeExtent(modifierLabelmapExtent[0], modifierLabelmapExtent[1], modifierLabelmapExtent[2], modifierLabelmapExtent[3], int(round(boundsIjk[4])), int(round(boundsIjk[5]))) polyToStencil.Update() stencilData = polyToStencil.GetOutput() stencilExtent = [0, -1, 0, -1, 0, -1] stencilData.SetExtent(stencilExtent) stencilToImage = vtk.vtkImageStencilToImage() stencilToImage.SetInputConnection(polyToStencil.GetOutputPort()) stencilToImage.SetInsideValue(1.0) stencilToImage.SetOutsideValue(0.0) stencilToImage.SetOutputScalarType(modifierLabelmap.GetScalarType()) stencilPositioner = vtk.vtkImageChangeInformation() stencilPositioner.SetInputConnection(stencilToImage.GetOutputPort()) stencilPositioner.SetOutputSpacing(modifierLabelmap.GetSpacing()) stencilPositioner.SetOutputOrigin(modifierLabelmap.GetOrigin()) stencilPositioner.Update() orientedStencilPositionerOutput = vtkSegmentationCore.vtkOrientedImageData() orientedStencilPositionerOutput.ShallowCopy(stencilToImage.GetOutput()) imageToWorld = vtk.vtkMatrix4x4() modifierLabelmap.GetImageToWorldMatrix(imageToWorld) orientedStencilPositionerOutput.SetImageToWorldMatrix(imageToWorld) vtkSegmentationCore.vtkOrientedImageDataResample.ModifyImage( modifierLabelmap, orientedStencilPositionerOutput, vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MAXIMUM) modeName = self.scriptedEffect.parameter("Mode") if modeName == "EMBOSS": mode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd elif modeName == "ENGRAVE": mode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove else: logging.error("Invalid mode: "+modeName+" (valid modes: EMBOSS, ENGRAVE)") self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, mode) # get plane parameters as space-separated string list planeParameters = [] if segmentMarkupNode.GetIsPlaneValid(): planeParameters.extend(segmentMarkupNode.GetOriginWorld()) xAxis = [1, 0, 0] yAxis = [0, 1, 0] zAxis = [0, 0, 1] segmentMarkupNode.GetAxesWorld(xAxis, yAxis, zAxis) planeParameters.extend(xAxis) planeParameters.extend(yAxis) planeParameters.extend(zAxis) planeParameters.extend(segmentMarkupNode.GetPlaneBounds()) planeParametersString = ' '.join(map(str, planeParameters)) segmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() segment = segmentationNode.GetSegmentation().GetSegment(segmentID) segment.SetTag("EngraveEffectPlaneParameters", planeParametersString)
def cutSurfaceWithModel(self, segmentMarkupNode, segmentModel): import vtkSegmentationCore if not segmentMarkupNode: raise AttributeError("{}: segment markup node not set.".format( self.__class__.__name__)) if not segmentModel: raise AttributeError("{}: segment model not set.".format( self.__class__.__name__)) if segmentMarkupNode and segmentModel.GetPolyData().GetNumberOfCells( ) > 0: segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() if not segmentationNode: raise AttributeError("{}: Segmentation node not set.".format( self.__class__.__name__)) modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() if not modifierLabelmap: raise AttributeError( "{}: ModifierLabelmap not set. This can happen for various reasons:\n" "No master volume set for segmentation,\n" "No existing segments for segmentation, or\n" "No referenceImageGeometry is specified in the segmentation" .format(self.__class__.__name__)) WorldToModifierLabelmapIjkTransform = vtk.vtkTransform() WorldToModifierLabelmapIjkTransformer = vtk.vtkTransformPolyDataFilter( ) WorldToModifierLabelmapIjkTransformer.SetTransform( WorldToModifierLabelmapIjkTransform) WorldToModifierLabelmapIjkTransformer.SetInputConnection( segmentModel.GetPolyDataConnection()) segmentationToSegmentationIjkTransformMatrix = vtk.vtkMatrix4x4() modifierLabelmap.GetImageToWorldMatrix( segmentationToSegmentationIjkTransformMatrix) segmentationToSegmentationIjkTransformMatrix.Invert() WorldToModifierLabelmapIjkTransform.Concatenate( segmentationToSegmentationIjkTransformMatrix) worldToSegmentationTransformMatrix = vtk.vtkMatrix4x4() slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes( None, segmentationNode.GetParentTransformNode(), worldToSegmentationTransformMatrix) WorldToModifierLabelmapIjkTransform.Concatenate( worldToSegmentationTransformMatrix) WorldToModifierLabelmapIjkTransformer.Update() polyToStencil = vtk.vtkPolyDataToImageStencil() polyToStencil.SetOutputSpacing(1.0, 1.0, 1.0) polyToStencil.SetInputConnection( WorldToModifierLabelmapIjkTransformer.GetOutputPort()) boundsIjk = WorldToModifierLabelmapIjkTransformer.GetOutput( ).GetBounds() modifierLabelmapExtent = self.scriptedEffect.modifierLabelmap( ).GetExtent() polyToStencil.SetOutputWholeExtent(modifierLabelmapExtent[0], modifierLabelmapExtent[1], modifierLabelmapExtent[2], modifierLabelmapExtent[3], int(round(boundsIjk[4])), int(round(boundsIjk[5]))) polyToStencil.Update() stencilData = polyToStencil.GetOutput() stencilExtent = [0, -1, 0, -1, 0, -1] stencilData.SetExtent(stencilExtent) stencilToImage = vtk.vtkImageStencilToImage() stencilToImage.SetInputConnection(polyToStencil.GetOutputPort()) stencilToImage.SetInsideValue(1.0) stencilToImage.SetOutsideValue(0.0) stencilToImage.SetOutputScalarType( modifierLabelmap.GetScalarType()) stencilPositioner = vtk.vtkImageChangeInformation() stencilPositioner.SetInputConnection( stencilToImage.GetOutputPort()) stencilPositioner.SetOutputSpacing(modifierLabelmap.GetSpacing()) stencilPositioner.SetOutputOrigin(modifierLabelmap.GetOrigin()) stencilPositioner.Update() orientedStencilPositionerOutput = vtkSegmentationCore.vtkOrientedImageData( ) orientedStencilPositionerOutput.ShallowCopy( stencilToImage.GetOutput()) imageToWorld = vtk.vtkMatrix4x4() modifierLabelmap.GetImageToWorldMatrix(imageToWorld) orientedStencilPositionerOutput.SetImageToWorldMatrix(imageToWorld) vtkSegmentationCore.vtkOrientedImageDataResample.ModifyImage( modifierLabelmap, orientedStencilPositionerOutput, vtkSegmentationCore.vtkOrientedImageDataResample. OPERATION_MAXIMUM) self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) # get fiducial positions as space-separated list import numpy n = segmentMarkupNode.GetNumberOfFiducials() fPos = [] for i in xrange(n): coord = [0.0, 0.0, 0.0] segmentMarkupNode.GetNthFiducialPosition(i, coord) fPos.extend(coord) fPosString = ' '.join(map(str, fPos)) segmentID = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() segment = segmentationNode.GetSegmentation().GetSegment(segmentID) segment.SetTag("DrawTubeEffectMarkupPositions", fPosString)
def TestSection_AddRemoveSegment(self): # Add/remove segment from segmentation (check display properties, color table, etc.) logging.info('Test section: Add/remove segment') # Get baseline values displayNode = self.inputSegmentationNode.GetDisplayNode() self.assertIsNotNone(displayNode) # If segments are not found then the returned color is the pre-defined invalid color bodyColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.bodySegmentName).GetColor() logging.info(f"bodyColor: {bodyColor}") self.assertEqual(int(bodyColor[0] * 100), 33) self.assertEqual(int(bodyColor[1] * 100), 66) self.assertEqual(int(bodyColor[2] * 100), 0) tumorColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.tumorSegmentName).GetColor() logging.info(f"tumorColor: {tumorColor}") self.assertEqual(int(tumorColor[0] * 100), 100) self.assertEqual(int(tumorColor[1] * 100), 0) self.assertEqual(int(tumorColor[2] * 100), 0) # Create new segment sphere = vtk.vtkSphereSource() sphere.SetCenter(0, 50, 0) sphere.SetRadius(80) sphere.Update() spherePolyData = vtk.vtkPolyData() spherePolyData.DeepCopy(sphere.GetOutput()) self.sphereSegment = vtkSegmentationCore.vtkSegment() self.sphereSegment.SetName(self.sphereSegmentName) self.sphereSegment.SetColor(0.0, 0.0, 1.0) self.sphereSegment.AddRepresentation(self.closedSurfaceReprName, spherePolyData) # Add segment to segmentation self.inputSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3) # Check merged labelmap mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName) self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0) imageStat = vtk.vtkImageAccumulate() imageStat.SetInputData(mergedLabelmap) imageStat.SetComponentExtent(0, 4, 0, 0, 0, 0) imageStat.SetComponentOrigin(0, 0, 0) imageStat.SetComponentSpacing(1, 1, 1) imageStat.Update() imageStatResult = imageStat.GetOutput() for i in range(4): logging.info(f"Volume {i}: {imageStatResult.GetScalarComponentAsDouble(i,0,0,0)}") self.assertEqual(imageStat.GetVoxelCount(), 1000) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 786) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 170) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 4) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 40) # Check if segment reorder is taken into account in merged labelmap generation # Change segment order sphereSegmentId = self.inputSegmentationNode.GetSegmentation().GetSegmentIdBySegment(self.sphereSegment) self.inputSegmentationNode.GetSegmentation().SetSegmentIndex(sphereSegmentId, 1) # Re-generate merged labelmap self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0) imageStat.SetInputData(mergedLabelmap) imageStat.Update() imageStatResult = imageStat.GetOutput() for i in range(4): logging.info(f"Volume {i}: {imageStatResult.GetScalarComponentAsDouble(i,0,0,0)}") self.assertEqual(imageStat.GetVoxelCount(), 1000) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 786) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 170) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 39) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 5) # Remove segment from segmentation self.inputSegmentationNode.GetSegmentation().RemoveSegment(self.sphereSegmentName) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2)
def exportAsDICOMSEG(self, exportablesCollection): """Export the given node to a segmentation object and load it in the DICOM database This function was copied and modified from the EditUtil.py function of the same name in Slicer. """ import logging if hasattr(slicer.modules, 'segmentations'): exportable = exportablesCollection.GetItemAsObject(0) subjectHierarchyNode = slicer.mrmlScene.GetNodeByID(exportable.GetNodeID()) instanceUIDs = subjectHierarchyNode.GetAttribute("DICOM.ReferencedInstanceUIDs").split() if instanceUIDs == "": raise Exception("Editor master node does not have DICOM information") # get the list of source DICOM files inputDICOMImageFileNames = "" for instanceUID in instanceUIDs: inputDICOMImageFileNames += slicer.dicomDatabase.fileForInstance(instanceUID) + "," inputDICOMImageFileNames = inputDICOMImageFileNames[:-1] # strip last comma # save the per-structure volumes in the temp directory inputSegmentationsFileNames = "" import random # TODO: better way to generate temp file names? import vtkITK writer = vtkITK.vtkITKImageWriter() rasToIJKMatrix = vtk.vtkMatrix4x4() import vtkSegmentationCore import vtkSlicerSegmentationsModuleLogic logic = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic() segmentationTransform = vtk.vtkMatrix4x4() segmentationNode = subjectHierarchyNode.GetAssociatedNode() mergedSegmentationImageData = segmentationNode.GetImageData() mergedSegmentationLabelmapNode = slicer.vtkMRMLLabelMapVolumeNode() segmentationNode.GetRASToIJKMatrix(rasToIJKMatrix) mergedSegmentationLabelmapNode.SetRASToIJKMatrix(rasToIJKMatrix) mergedSegmentationLabelmapNode.SetAndObserveImageData(mergedSegmentationImageData) mergedSegmentationOrientedImageData = logic.CreateOrientedImageDataFromVolumeNode(mergedSegmentationLabelmapNode) segmentation = segmentationNode.GetSegmentation() segmentIDs = vtk.vtkStringArray() segmentation.GetSegmentIDs(segmentIDs) segmentationName = segmentationNode.GetName() for i in range(0, segmentIDs.GetNumberOfValues()): segmentID = segmentIDs.GetValue(i) segment = segmentation.GetSegment(segmentID) segmentName = segment.GetName() structureName = segmentName[len(segmentationName)+1:-1*len('-label')] structureFileName = structureName + str(random.randint(0,vtk.VTK_INT_MAX)) + ".nrrd" filePath = os.path.join(slicer.app.temporaryPath, structureFileName) writer.SetFileName(filePath) segmentImageData = segment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) paddedImageData = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkOrientedImageDataResample.PadImageToContainImage(segmentImageData, mergedSegmentationOrientedImageData, paddedImageData) labelmapImageData = slicer.vtkMRMLLabelMapVolumeNode() logic.CreateLabelmapVolumeFromOrientedImageData(paddedImageData, labelmapImageData) writer.SetInputDataObject(labelmapImageData.GetImageData()) labelmapImageData.GetRASToIJKMatrix(rasToIJKMatrix) writer.SetRasToIJKMatrix(rasToIJKMatrix) logging.debug("Saving to %s..." % filePath) writer.Write() inputSegmentationsFileNames += filePath + "," inputSegmentationsFileNames = inputSegmentationsFileNames[:-1] # strip last comma # save the per-structure volumes label attributes colorNode = segmentationNode.GetNodeReference('colorNodeID') terminologyName = colorNode.GetAttribute("TerminologyName") colorLogic = slicer.modules.colors.logic() if not terminologyName or not colorLogic: raise Exception("No terminology or color logic - cannot export") inputLabelAttributesFileNames = "" for i in range(0, segmentIDs.GetNumberOfValues()): segmentID = segmentIDs.GetValue(i) segment = segmentation.GetSegment(segmentID) segmentName = segment.GetName() structureName = segmentName[len(segmentationName)+1:-1*len('-label')] labelIndex = colorNode.GetColorIndexByName( structureName ) rgbColor = [0,]*4 colorNode.GetColor(labelIndex, rgbColor) rgbColor = map(lambda e: e*255., rgbColor) # get the attributes and conver to format CodeValue,CodeMeaning,CodingSchemeDesignator # or empty strings if not defined propertyCategoryWithColons = colorLogic.GetSegmentedPropertyCategory(labelIndex, terminologyName) if propertyCategoryWithColons == '': logging.debug ('ERROR: no segmented property category found for label ',str(labelIndex)) # Try setting a default as this section is required propertyCategory = "C94970,NCIt,Reference Region" else: propertyCategory = propertyCategoryWithColons.replace(':',',') propertyTypeWithColons = colorLogic.GetSegmentedPropertyType(labelIndex, terminologyName) propertyType = propertyTypeWithColons.replace(':',',') propertyTypeModifierWithColons = colorLogic.GetSegmentedPropertyTypeModifier(labelIndex, terminologyName) propertyTypeModifier = propertyTypeModifierWithColons.replace(':',',') anatomicRegionWithColons = colorLogic.GetAnatomicRegion(labelIndex, terminologyName) anatomicRegion = anatomicRegionWithColons.replace(':',',') anatomicRegionModifierWithColons = colorLogic.GetAnatomicRegionModifier(labelIndex, terminologyName) anatomicRegionModifier = anatomicRegionModifierWithColons.replace(':',',') structureFileName = structureName + str(random.randint(0,vtk.VTK_INT_MAX)) + ".info" filePath = os.path.join(slicer.app.temporaryPath, structureFileName) # EncodeSEG is expecting a file of format: # labelNum;SegmentedPropertyCategory:codeValue,codeScheme,codeMeaning;SegmentedPropertyType:v,m,s etc attributes = "%d" % labelIndex attributes += ";SegmentedPropertyCategory:"+propertyCategory if propertyType != "": attributes += ";SegmentedPropertyType:" + propertyType if propertyTypeModifier != "": attributes += ";SegmentedPropertyTypeModifier:" + propertyTypeModifier if anatomicRegion != "": attributes += ";AnatomicRegion:" + anatomicRegion if anatomicRegionModifier != "": attributes += ";AnatomicRegionModifer:" + anatomicRegionModifier attributes += ";SegmentAlgorithmType:AUTOMATIC" attributes += ";SegmentAlgorithmName:SlicerSelfTest" attributes += ";RecommendedDisplayRGBValue:%g,%g,%g" % tuple(rgbColor[:-1]) fp = open(filePath, "w") fp.write(attributes) fp.close() logging.debug ("filePath: %s", filePath) logging.debug ("attributes: %s", attributes) inputLabelAttributesFileNames += filePath + "," inputLabelAttributesFileNames = inputLabelAttributesFileNames[:-1] # strip last comma''' try: user = os.environ['USER'] except KeyError: user = "******" segFileName = "editor_export.SEG" + str(random.randint(0,vtk.VTK_INT_MAX)) + ".dcm" segFilePath = os.path.join(slicer.app.temporaryPath, segFileName) # TODO: define a way to set parameters like description # TODO: determine a good series number automatically by looking in the database parameters = { "inputDICOMImageFileNames": inputDICOMImageFileNames, "inputSegmentationsFileNames": inputSegmentationsFileNames, "inputLabelAttributesFileNames": inputLabelAttributesFileNames, "readerId": user, "sessionId": "1", "timePointId": "1", "seriesDescription": "SlicerEditorSEGExport", "seriesNumber": "100", "instanceNumber": "1", "bodyPart": "HEAD", "algorithmDescriptionFileName": "Editor", "outputSEGFileName": segFilePath, "skipEmptySlices": False, "compress": False, } encodeSEG = slicer.modules.encodeseg cliNode = None cliNode = slicer.cli.run(encodeSEG, cliNode, parameters, delete_temporary_files=False) waitCount = 0 while cliNode.IsBusy() and waitCount < 20: slicer.util.delayDisplay( "Running SEG Encoding... %d" % waitCount, 1000 ) waitCount += 1 if cliNode.GetStatusString() != 'Completed': raise Exception("encodeSEG CLI did not complete cleanly") logging.info("Added segmentation to DICOM database (%s)", segFilePath) slicer.dicomDatabase.insert(segFilePath)
def TestSection_MaskingSettings(self): self.segmentation.RemoveAllSegments() segment1Id = self.segmentation.AddEmptySegment("Segment_1") segment2Id = self.segmentation.AddEmptySegment("Segment_2") segment3Id = self.segmentation.AddEmptySegment("Segment_3") segment4Id = self.segmentation.AddEmptySegment("Segment_4") oldOverwriteMode = self.segmentEditorNode.GetOverwriteMode() #------------------- # Test applying threshold with no masking self.segmentEditorNode.SetSelectedSegmentID(segment1Id) self.thresholdEffect.setParameter("MinimumThreshold", "-17") self.thresholdEffect.setParameter("MaximumThreshold", "848") self.thresholdEffect.self().onApply() self.checkSegmentVoxelCount(0, 204) # Segment_1 self.checkSegmentVoxelCount(1, 0) # Segment_2 #------------------- # Add paint to segment 2. No overwrite paintModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData() paintModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas) paintModifierLabelmap.SetExtent(2, 5, 2, 5, 2, 5) paintModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) paintModifierLabelmap.GetPointData().GetScalars().Fill(1) self.segmentEditorNode.SetOverwriteMode( self.segmentEditorNode.OverwriteNone) self.segmentEditorNode.SetSelectedSegmentID(segment2Id) self.paintEffect.modifySelectedSegmentByLabelmap( paintModifierLabelmap, self.paintEffect.ModificationModeAdd) self.checkSegmentVoxelCount(0, 204) # Segment_1 self.checkSegmentVoxelCount(1, 64) # Segment_2 #------------------- # Test erasing with no masking eraseModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData() eraseModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas) eraseModifierLabelmap.SetExtent(2, 5, 2, 5, 2, 5) eraseModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) eraseModifierLabelmap.GetPointData().GetScalars().Fill(1) self.segmentEditorNode.SetSelectedSegmentID(segment1Id) self.eraseEffect.modifySelectedSegmentByLabelmap( eraseModifierLabelmap, self.paintEffect.ModificationModeRemove) self.checkSegmentVoxelCount(0, 177) # Segment_1 self.checkSegmentVoxelCount(1, 64) # Segment_2 #------------------- # Test erasing with masking on empty segment self.segmentEditorNode.SetSelectedSegmentID(segment1Id) self.thresholdEffect.self().onApply() # Reset Segment_1 self.checkSegmentVoxelCount(0, 204) # Segment_1 self.segmentEditorNode.SetMaskMode( slicer.vtkMRMLSegmentationNode.EditAllowedInsideSingleSegment) self.segmentEditorNode.SetMaskSegmentID(segment2Id) self.eraseEffect.modifySelectedSegmentByLabelmap( eraseModifierLabelmap, self.paintEffect.ModificationModeRemove) self.checkSegmentVoxelCount( 0, 177 ) # We expect to be able to erase the current segment regardless of masking self.checkSegmentVoxelCount(1, 64) # Segment_2 #------------------- # Test erasing with masking on the same segment self.segmentEditorNode.SetSelectedSegmentID(segment1Id) self.thresholdEffect.self().onApply() # Reset Segment_1 self.checkSegmentVoxelCount(0, 204) # Segment_1 self.segmentEditorNode.SetMaskSegmentID(segment1Id) self.eraseEffect.modifySelectedSegmentByLabelmap( eraseModifierLabelmap, self.paintEffect.ModificationModeRemove) self.checkSegmentVoxelCount(0, 177) # Segment_1 self.checkSegmentVoxelCount(1, 64) # Segment_2 #------------------- # Test erasing all segments self.segmentEditorNode.SetMaskMode( slicer.vtkMRMLSegmentationNode.EditAllowedEverywhere) self.thresholdEffect.self().onApply() # Reset Segment_1 self.checkSegmentVoxelCount(0, 204) # Segment_1 self.segmentEditorNode.SetSelectedSegmentID(segment1Id) self.eraseEffect.modifySelectedSegmentByLabelmap( eraseModifierLabelmap, self.paintEffect.ModificationModeRemoveAll) self.checkSegmentVoxelCount(0, 177) # Segment_1 self.checkSegmentVoxelCount(1, 0) # Segment_2 #------------------- # Test adding back segments self.thresholdEffect.self().onApply() # Reset Segment_1 self.checkSegmentVoxelCount(0, 204) # Segment_1 self.segmentEditorNode.SetMaskMode( slicer.vtkMRMLSegmentationNode.EditAllowedInsideSingleSegment) self.segmentEditorNode.SetMaskSegmentID(segment2Id) self.eraseEffect.modifySelectedSegmentByLabelmap( eraseModifierLabelmap, self.paintEffect.ModificationModeRemove) self.checkSegmentVoxelCount(0, 177) # Segment_1 self.checkSegmentVoxelCount(1, 27) # Segment_2 #------------------- # Test threshold effect segment mask self.segmentEditorNode.SetMaskSegmentID(segment2Id) # Erase Segment_2 self.segmentEditorNode.SetSelectedSegmentID(segment2Id) self.eraseEffect.modifySelectedSegmentByLabelmap( eraseModifierLabelmap, self.paintEffect.ModificationModeRemove) self.segmentEditorNode.SetMaskSegmentID(segment1Id) self.segmentEditorNode.SetSelectedSegmentID(segment2Id) self.thresholdEffect.self().onApply( ) # Threshold Segment_2 within Segment_1 self.checkSegmentVoxelCount(0, 177) # Segment_1 self.checkSegmentVoxelCount(1, 177) # Segment_2 #------------------- # Test intensity masking with segment mask self.segmentEditorNode.MasterVolumeIntensityMaskOn() self.segmentEditorNode.SetMasterVolumeIntensityMaskRange(-17, 848) self.thresholdEffect.setParameter("MinimumThreshold", "-99999") self.thresholdEffect.setParameter("MaximumThreshold", "99999") self.segmentEditorNode.SetSelectedSegmentID(segment3Id) self.thresholdEffect.self().onApply() # Threshold Segment_3 self.checkSegmentVoxelCount(2, 177) # Segment_3 #------------------- # Test intensity masking with islands self.segmentEditorNode.SetMaskMode( slicer.vtkMRMLSegmentationNode.EditAllowedEverywhere) self.segmentEditorNode.MasterVolumeIntensityMaskOff() self.segmentEditorNode.SetSelectedSegmentID(segment4Id) island1ModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData() island1ModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas) island1ModifierLabelmap.SetExtent(2, 5, 2, 5, 2, 5) island1ModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) island1ModifierLabelmap.GetPointData().GetScalars().Fill(1) self.paintEffect.modifySelectedSegmentByLabelmap( island1ModifierLabelmap, self.paintEffect.ModificationModeAdd) island2ModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData() island2ModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas) island2ModifierLabelmap.SetExtent(7, 9, 7, 9, 7, 9) island2ModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) island2ModifierLabelmap.GetPointData().GetScalars().Fill(1) self.paintEffect.modifySelectedSegmentByLabelmap( island2ModifierLabelmap, self.paintEffect.ModificationModeAdd) self.checkSegmentVoxelCount(3, 91) # Segment_4 # Test that no masking works as expected minimumSize = 3 self.islandEffect.setParameter('MinimumSize', minimumSize) self.islandEffect.setParameter('Operation', 'KEEP_LARGEST_ISLAND') self.islandEffect.self().onApply() self.checkSegmentVoxelCount(3, 64) # Segment_4 # Reset Segment_4 islands self.paintEffect.modifySelectedSegmentByLabelmap( island1ModifierLabelmap, self.paintEffect.ModificationModeAdd) self.paintEffect.modifySelectedSegmentByLabelmap( island2ModifierLabelmap, self.paintEffect.ModificationModeAdd) # Test intensity masking self.segmentEditorNode.MasterVolumeIntensityMaskOn() self.segmentEditorNode.SetMasterVolumeIntensityMaskRange(-17, 848) self.islandEffect.self().onApply() self.checkSegmentVoxelCount(3, 87) # Segment_4 # Restore old overwrite setting self.segmentEditorNode.SetOverwriteMode(oldOverwriteMode) self.segmentEditorNode.MasterVolumeIntensityMaskOff()
def TestSection_AddRemoveSegment(self): # Add/remove segment from segmentation (check display properties, color table, etc.) logging.info('Test section: Add/remove segment') # Get baseline values displayNode = self.inputSegmentationNode.GetDisplayNode() self.assertIsNotNone(displayNode) # If segments are not found then the returned color is the pre-defined invalid color bodyColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.bodySegmentName).GetColor() logging.info("bodyColor: {0}".format(bodyColor)) self.assertEqual(int(bodyColor[0]*100), 33) self.assertEqual(int(bodyColor[1]*100), 66) self.assertEqual(int(bodyColor[2]*100), 0) tumorColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.tumorSegmentName).GetColor() logging.info("tumorColor: {0}".format(tumorColor)) self.assertEqual(int(tumorColor[0]*100), 100) self.assertEqual(int(tumorColor[1]*100), 0) self.assertEqual(int(tumorColor[2]*100), 0) # Create new segment sphere = vtk.vtkSphereSource() sphere.SetCenter(0,50,0) sphere.SetRadius(80) sphere.Update() spherePolyData = vtk.vtkPolyData() spherePolyData.DeepCopy(sphere.GetOutput()) self.sphereSegment = vtkSegmentationCore.vtkSegment() self.sphereSegment.SetName(self.sphereSegmentName) self.sphereSegment.SetColor(0.0,0.0,1.0) self.sphereSegment.AddRepresentation(self.closedSurfaceReprName, spherePolyData) # Add segment to segmentation self.inputSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3) # Check merged labelmap mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName) self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0) imageStat = vtk.vtkImageAccumulate() imageStat.SetInputData(mergedLabelmap) imageStat.SetComponentExtent(0,4,0,0,0,0) imageStat.SetComponentOrigin(0,0,0) imageStat.SetComponentSpacing(1,1,1) imageStat.Update() imageStatResult = imageStat.GetOutput() for i in range(4): logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0))) self.assertEqual(imageStat.GetVoxelCount(), 1000) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 786) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 170) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 4) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 40) # Check if segment reorder is taken into account in merged labelmap generation # Change segment order sphereSegmentId = self.inputSegmentationNode.GetSegmentation().GetSegmentIdBySegment(self.sphereSegment) self.inputSegmentationNode.GetSegmentation().SetSegmentIndex(sphereSegmentId, 1) # Re-generate merged labelmap self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0) imageStat.SetInputData(mergedLabelmap) imageStat.Update() imageStatResult = imageStat.GetOutput() for i in range(4): logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0))) self.assertEqual(imageStat.GetVoxelCount(), 1000) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 786) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 170) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 39) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 5) # Remove segment from segmentation self.inputSegmentationNode.GetSegmentation().RemoveSegment(self.sphereSegmentName) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2)
def TestSection_MergeLabelmapWithDifferentGeometries(self): # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions) logging.info('Test section: Merge labelmap with different geometries') self.assertIsNotNone(self.sphereSegment) self.sphereSegment.RemoveRepresentation(self.binaryLabelmapReprName) self.assertIsNone( self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName)) # Create new segmentation with sphere segment self.secondSegmentationNode = slicer.vtkMRMLSegmentationNode() self.secondSegmentationNode.SetName('Second') self.secondSegmentationNode.GetSegmentation( ).SetMasterRepresentationName(self.binaryLabelmapReprName) slicer.mrmlScene.AddNode(self.secondSegmentationNode) self.secondSegmentationNode.GetSegmentation().AddSegment( self.sphereSegment) # Check automatically converted labelmap. It is supposed to have the default geometry # (which is different than the one in the input segmentation) sphereLabelmap = self.sphereSegment.GetRepresentation( self.binaryLabelmapReprName) self.assertIsNotNone(sphereLabelmap) sphereLabelmapSpacing = sphereLabelmap.GetSpacing() self.assertTrue(sphereLabelmapSpacing[0] == 1.0 and sphereLabelmapSpacing[1] == 1.0 and sphereLabelmapSpacing[2] == 1.0) # Create binary labelmap in segmentation that will create the merged labelmap from # different geometries so that labelmap is not removed from sphere segment when adding self.inputSegmentationNode.GetSegmentation().CreateRepresentation( self.binaryLabelmapReprName) # Copy segment to input segmentation self.inputSegmentationNode.GetSegmentation( ).CopySegmentFromSegmentation( self.secondSegmentationNode.GetSegmentation(), self.sphereSegmentName) self.assertEqual( self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3) # Check merged labelmap # Reference geometry has the tiny patient spacing, and it is oversampled to have smimilar # voxel size as the sphere labelmap with the uniform 1mm spacing mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments( mergedLabelmap, 0) mergedLabelmapSpacing = mergedLabelmap.GetSpacing() self.assertAlmostEqual(mergedLabelmapSpacing[0], 1.2894736842, 8) self.assertAlmostEqual(mergedLabelmapSpacing[1], 1.2894736842, 8) self.assertAlmostEqual(mergedLabelmapSpacing[2], 0.6052631578, 8) imageStat = vtk.vtkImageAccumulate() imageStat.SetInputData(mergedLabelmap) imageStat.SetComponentExtent(0, 5, 0, 0, 0, 0) imageStat.SetComponentOrigin(0, 0, 0) imageStat.SetComponentSpacing(1, 1, 1) imageStat.Update() self.assertEqual(imageStat.GetVoxelCount(), 54872000) imageStatResult = imageStat.GetOutput() for i in range(5): logging.info("Volume {0}: {1}".format( i, imageStatResult.GetScalarComponentAsDouble(i, 0, 0, 0))) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 43573723) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 10601312) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 251476) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 445489) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(4, 0, 0, 0), 0 ) # Built from color table and color four is removed in previous test section
def growCut(self): # Get master volume image data import vtkSegmentationCore masterImageData = vtkSegmentationCore.vtkOrientedImageData() self.scriptedEffect.masterVolumeImageData(masterImageData) # Get segmentation import vtkSlicerSegmentationsModuleMRML segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() # Cast master image if not short if masterImageData.GetScalarType() != vtk.VTK_SHORT: imageCast = vtk.vtkImageCast() imageCast.SetInputData(masterImageData) imageCast.SetOutputScalarTypeToShort() imageCast.ClampOverflowOn() imageCast.Update() masterImageDataShort = vtkSegmentationCore.vtkOrientedImageData() masterImageDataShort.DeepCopy(imageCast.GetOutput()) # Copy image data masterImageDataShort.CopyDirections(masterImageData) # Copy geometry masterImageData = masterImageDataShort # Generate merged labelmap as input to GrowCut mergedImage = vtk.vtkImageData() mergedImageToWorldMatrix = vtk.vtkMatrix4x4() segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, mergedImageToWorldMatrix, masterImageData) # Make a zero-valued volume for the output outputLabelmap = vtkSegmentationCore.vtkOrientedImageData() thresh = vtk.vtkImageThreshold() thresh.ReplaceInOn() thresh.ReplaceOutOn() thresh.SetInValue(0) thresh.SetOutValue(0) thresh.SetOutputScalarType( vtk.VTK_SHORT ) thresh.SetInputData( mergedImage ) thresh.SetOutput( outputLabelmap ) thresh.Update() outputLabelmap.DeepCopy( mergedImage ) #TODO: It was thresholded just above, why deep copy now? # Perform grow cut growCutFilter = vtkITK.vtkITKGrowCutSegmentationImageFilter() growCutFilter.SetInputData( 0, masterImageData ) growCutFilter.SetInputData( 1, mergedImage ) #TODO: This call sets an empty image for the optional "previous segmentation", and # is apparently needed for the first segmentation too. Why? growCutFilter.SetInputConnection( 2, thresh.GetOutputPort() ) #TODO: These are magic numbers inherited from EditorLib/GrowCut.py objectSize = 5. contrastNoiseRatio = 0.8 priorStrength = 0.003 segmented = 2 conversion = 1000 spacing = mergedImage.GetSpacing() voxelVolume = reduce(lambda x,y: x*y, spacing) voxelAmount = objectSize / voxelVolume voxelNumber = round(voxelAmount) * conversion cubeRoot = 1./3. oSize = int(round(pow(voxelNumber,cubeRoot))) growCutFilter.SetObjectSize( oSize ) growCutFilter.SetContrastNoiseRatio( contrastNoiseRatio ) growCutFilter.SetPriorSegmentConfidence( priorStrength ) growCutFilter.Update() outputLabelmap.DeepCopy( growCutFilter.GetOutput() ) # Write output segmentation results in segments import vtkSlicerSegmentationsModuleLogic segmentIDs = vtk.vtkStringArray() segmentationNode.GetSegmentation().GetSegmentIDs(segmentIDs) for index in xrange(segmentIDs.GetNumberOfValues()): segmentID = segmentIDs.GetValue(index) segment = segmentationNode.GetSegmentation().GetSegment(segmentID) # Get label corresponding to segment in merged labelmap (and so GrowCut output) colorIndexStr = vtk.mutable("") tagFound = segment.GetTag(vtkSlicerSegmentationsModuleMRML.vtkMRMLSegmentationDisplayNode.GetColorIndexTag(), colorIndexStr); if not tagFound: logging.error('Failed to apply GrowCut result on segment ' + segmentID) continue colorIndex = int(colorIndexStr.get()) # 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) thresh.ThresholdBetween(colorIndex, colorIndex); thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) thresh.SetInputData(outputLabelmap) thresh.Update() # Write label to segment newSegmentLabelmap = vtkSegmentationCore.vtkOrientedImageData() newSegmentLabelmap.ShallowCopy(thresh.GetOutput()) newSegmentLabelmap.SetGeometryFromImageToWorldMatrix(mergedImageToWorldMatrix) vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, segmentationNode, segmentID)