def onApply(self): # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) self.scriptedEffect.saveStateForUndo() # Get parameters fullyConnected = self.scriptedEffect.integerParameter("FullyConnected") minimumSize = self.scriptedEffect.integerParameter("MinimumSize") # Get modifier labelmap selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(selectedSegmentLabelmap) 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 vtkSegmentationCorePython as vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(castOut.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() selectedSegmentName = segmentationNode.GetSegmentation().GetSegment( selectedSegmentID).GetName() slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \ multiLabelImage, segmentationNode, selectedSegmentName ) segmentationNode.GetSegmentation().RemoveSegment(selectedSegmentID) qt.QApplication.restoreOverrideCursor()
def findLargest2DRegion(segmentationNode): qrLogic = CustomSegmentEditorLogic segmentationsLogic = slicer.modules.segmentations.logic() largestLM = None largestSize = 0 for segment in qrLogic.getAllSegments(segmentationNode): imageData = vtkSegmentationCore.vtkOrientedImageData() segmentID = segmentationNode.GetSegmentation( ).GetSegmentIdBySegment(segment) segmentationsLogic.GetSegmentBinaryLabelmapRepresentation( segmentationNode, segmentID, imageData) extent = imageData.GetExtent() if extent[1] != -1 and extent[3] != -1 and extent[5] != -1: tempLabel = slicer.vtkMRMLLabelMapVolumeNode() tempLabel.SetName(segment.GetName() + "CentroidHelper") segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData( imageData, tempLabel) dims = tempLabel.GetImageData().GetDimensions() size = dims[0] * dims[1] if size > largestSize: largestSize = size largestLM = tempLabel return largestLM
def onApply(self): import vtkSegmentationCorePython as vtkSegmentationCore # Get modifier labelmap and parameters operation = self.scriptedEffect.parameter("Operation") if operation in self.operationsRequireModifierSegment: # Get modifier segment segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() segmentation = segmentationNode.GetSegmentation() modifierSegmentID = self.modifierSegmentID() if not modifierSegmentID: logging.error("Operation {0} requires a selected modifier segment".format(operation)) return modifierSegment = segmentation.GetSegment(modifierSegmentID) modifierSegmentLabelmap = modifierSegment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) if operation == LOGICAL_COPY: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_UNION: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) elif operation == LOGICAL_SUBTRACT: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove) elif operation == LOGICAL_INTERSECT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() intersectionLabelmap = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage(selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap, vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent()) selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent() modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent() commonExtent = [max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]), min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]), max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]), min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]), max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]), min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5])] self.scriptedEffect.modifySelectedSegmentByLabelmap(intersectionLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent) elif operation == LOGICAL_INVERT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() inverter = vtk.vtkImageThreshold() inverter.SetInputData(selectedSegmentLabelmap) inverter.SetInValue(1) inverter.SetOutValue(0) inverter.ReplaceInOn() inverter.ThresholdByLower(0) inverter.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) inverter.Update() selectedSegmentLabelmap.DeepCopy(inverter.GetOutput()) self.scriptedEffect.modifySelectedSegmentByLabelmap(selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_CLEAR or operation == LOGICAL_FILL: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent()) self.scriptedEffect.modifySelectedSegmentByLabelmap(selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) else: logging.error("Uknown operation: {0}".format(operation))
def onApply(self): # TODO: # self.logic.undoRedo = self.undoRedo # Get parameters fullyConnected = self.scriptedEffect.integerParameter("FullyConnected") minimumSize = self.scriptedEffect.integerParameter("MinimumSize") # Get modifier labelmap selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(selectedSegmentLabelmap) 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 vtkSegmentationCorePython as vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(castOut.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName() slicer.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) displayNode.SetSegmentVisibility(selectedSegmentID, False)
def onApply(self): #TODO: #self.logic.undoRedo = self.undoRedo # Get parameters fullyConnected = self.scriptedEffect.integerParameter("FullyConnected") minimumSize = self.scriptedEffect.integerParameter("MinimumSize") # Get modifier labelmap selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(selectedSegmentLabelmap) 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 vtkSegmentationCorePython as vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(castOut.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName() slicer.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) displayNode.SetSegmentVisibility(selectedSegmentID, False)
def TestSection_2_MergeLabelmapWithDifferentGeometries(self): # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions) logging.info('Test section 2: 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 onApply(self): # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) self.scriptedEffect.saveStateForUndo() # Get parameters fullyConnected = self.scriptedEffect.integerParameter("FullyConnected") minimumSize = self.scriptedEffect.integerParameter("MinimumSize") # Get modifier labelmap selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(selectedSegmentLabelmap) 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 vtkSegmentationCorePython as vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(castOut.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName() slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \ multiLabelImage, segmentationNode, selectedSegmentName ) segmentationNode.GetSegmentation().RemoveSegment(selectedSegmentID) qt.QApplication.restoreOverrideCursor()
def TestSection_1_AddRemoveSegment(self): # Add/remove segment from segmentation (check display properties, color table, etc.) logging.info('Test section 1: 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 = displayNode.GetSegmentColor('Body_Contour') self.assertTrue(int(bodyColor[0]*100) == 33 and int(bodyColor[1]*100) == 66 and bodyColor[2] == 0.0) tumorColor = displayNode.GetSegmentColor('Tumor_Contour') self.assertTrue(tumorColor[0] == 1.0 and tumorColor[1] == 0.0 and tumorColor[2] == 0.0) # Create new segment sphere = vtk.vtkSphereSource() sphere.SetCenter(0,50,0) sphere.SetRadius(50) sphere.Update() spherePolyData = vtk.vtkPolyData() spherePolyData.DeepCopy(sphere.GetOutput()) self.sphereSegment = vtkSegmentationCore.vtkSegment() self.sphereSegment.SetName(self.sphereSegmentName) self.sphereSegment.SetDefaultColor(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) sphereColor = displayNode.GetSegmentColor(self.sphereSegmentName) self.assertTrue(sphereColor[0] == 0.0 and sphereColor[1] == 0.0 and sphereColor[2] == 1.0) # 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() self.assertEqual(imageStat.GetVoxelCount(), 1000) imageStatResult = imageStat.GetOutput() self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 795) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 194) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 5) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 6) # Remove segment from segmentation self.inputSegmentationNode.GetSegmentation().RemoveSegment(self.sphereSegmentName) self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2) sphereColor = displayNode.GetSegmentColor(self.sphereSegmentName) self.assertTrue(sphereColor[0] == 0.5 and sphereColor[1] == 0.5 and sphereColor[2] == 0.5)
def jumpToSegmentAndCreateScreenShot(segmentationNode, segment, widgets, center=False, crosshair=False): imageData = vtkSegmentationCore.vtkOrientedImageData() segmentID = segmentationNode.GetSegmentation().GetSegmentIdBySegment(segment) segmentationsLogic = slicer.modules.segmentations.logic() segmentationsLogic.GetSegmentBinaryLabelmapRepresentation(segmentationNode, segmentID, imageData) extent = imageData.GetExtent() if extent[1] != -1 and extent[3] != -1 and extent[5] != -1: tempLabel = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(tempLabel) tempLabel.SetName(segment.GetName() + "CentroidHelper") segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData(imageData, tempLabel) CustomSegmentEditorLogic.applyThreshold(tempLabel, 1) centroid = ModuleLogicMixin.getCentroidForLabel(tempLabel, 1) slicer.mrmlScene.RemoveNode(tempLabel) annotationNodes = [] crosshairButton = None if crosshair: crosshairButton = CrosshairButton() crosshairButton.setSliceIntersectionEnabled(True) crosshairButton.checked = True for widget in widgets: sliceLogic = widget.sliceLogic() sliceNode = sliceLogic.GetSliceNode() if not center: sliceNode.JumpSliceByOffsetting(centroid[0], centroid[1], centroid[2]) else: markupsLogic = slicer.modules.markups.logic() markupsLogic.JumpSlicesToLocation(centroid[0], centroid[1], centroid[2], True) dNodeProperties = ScreenShotHelper.saveSegmentDisplayProperties(segmentationNode, segment) segmentationNode.GetDisplayNode().SetAllSegmentsVisibility(False) ScreenShotHelper.setDisplayNodeProperties(segmentationNode, segment, properties={'fill': True, 'outline': True, 'visible': True}) if crosshairButton: crosshairButton.crosshairNode.SetCrosshairRAS(centroid) annotationNode = ScreenShotHelper.takeScreenShot("{}_Screenshot_{}_{}".format(segment.GetName(), sliceNode.GetName(), sliceNode.GetOrientation()), "", widget) segmentationNode.GetDisplayNode().SetAllSegmentsVisibility(True) ScreenShotHelper.setDisplayNodeProperties(segmentationNode, segment, dNodeProperties) annotationNodes.append(annotationNode) if crosshairButton: crosshairButton.checked = False return annotationNodes[0] if len(annotationNodes) == 1 else annotationNodes
def createLabelNodeFromSegment(segmentationNode, segmentID): labelNode = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(labelNode) segmentationsLogic = slicer.modules.segmentations.logic() mergedImageData = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImageData, 0, None, DICOMSegmentationExporter.vtkStringArrayFromList([segmentID])) if not segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData(mergedImageData, labelNode): slicer.mrmlScene.RemoveNode(labelNode) return None labelNode.SetName("{}_label".format(segmentID)) return labelNode
def onApply(self): # Apply changes import vtkSegmentationCorePython as vtkSegmentationCore segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() if (slicer.app.majorVersion >= 5) or (slicer.app.majorVersion >= 4 and slicer.app.minorVersion >= 11): modifierLabelmap = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GetBinaryLabelmapRepresentation(self.selectedSegmentId, modifierLabelmap) else: modifierLabelmap = segmentationNode.GetBinaryLabelmapRepresentation(self.selectedSegmentId) self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) self.originalSelectedSegmentLabelmap = None self.reset() self.scriptedEffect.selectEffect("")
def updateLabel(self,value): if not self.fm: return self.fm.show(value) self.fm.Modified() self.fm.Update() import vtkSegmentationCorePython as vtkSegmentationCore newSegmentLabelmap = vtkSegmentationCore.vtkOrientedImageData() newSegmentLabelmap.ShallowCopy(self.fm.GetOutput()) newSegmentLabelmap.CopyDirections(self.originalSelectedSegmentLabelmap) segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, segmentationNode, self.selectedSegmentId, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, newSegmentLabelmap.GetExtent())
def createLabelNodeFromSegment(segmentationNode, segmentID): labelNode = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(labelNode) segmentationsLogic = slicer.modules.segmentations.logic() mergedImageData = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments( mergedImageData, 0, None, DICOMSegmentationExporter.vtkStringArrayFromList([segmentID])) if not segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData( mergedImageData, labelNode): slicer.mrmlScene.RemoveNode(labelNode) return None labelNode.SetName("{}_label".format(segmentID)) return labelNode
def getSegmentCentroid(segmentationNode, segment): imageData = vtkSegmentationCore.vtkOrientedImageData() segmentID = segmentationNode.GetSegmentation().GetSegmentIdBySegment(segment) segmentationsLogic = slicer.modules.segmentations.logic() segmentationsLogic.GetSegmentBinaryLabelmapRepresentation(segmentationNode, segmentID, imageData) extent = imageData.GetExtent() if extent[1] != -1 and extent[3] != -1 and extent[5] != -1: tempLabel = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(tempLabel) tempLabel.SetName(segment.GetName() + "CentroidHelper") segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData(imageData, tempLabel) CustomSegmentEditorLogic.applyThreshold(tempLabel, 1) centroid = ModuleLogicMixin.getCentroidForLabel(tempLabel, 1) slicer.mrmlScene.RemoveNode(tempLabel) return centroid return None
def getInvertedBinaryLabelmap(self, modifierLabelmap): import vtkSegmentationCorePython as vtkSegmentationCore fillValue = 1 eraseValue = 0 inverter = vtk.vtkImageThreshold() inverter.SetInputData(modifierLabelmap) inverter.SetInValue(fillValue) inverter.SetOutValue(eraseValue) inverter.ReplaceInOn() inverter.ThresholdByLower(0) inverter.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) inverter.Update() invertedModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData() invertedModifierLabelmap.ShallowCopy(inverter.GetOutput()) imageToWorldMatrix = vtk.vtkMatrix4x4() modifierLabelmap.GetImageToWorldMatrix(imageToWorldMatrix) invertedModifierLabelmap.SetGeometryFromImageToWorldMatrix(imageToWorldMatrix) return invertedModifierLabelmap
def clipOrientedImageWithModel(self, inputOrientedImage, inputParentTransformNode, outputOrientedImage, outputParentTransformNode, clipOutsideSurface=True, fillValue=0, reduceExtent=False): """ Fill voxels of the input volume inside/outside the clipping model with the provided fill value """ import vtkSegmentationCorePython as vtkSegmentationCore # Determine the transform between the box and the image IJK coordinate systems rasToModel = vtk.vtkMatrix4x4() if self.roiModelNode.GetParentTransformNode() is not None: self.roiModelNode.GetParentTransformNode( ).GetMatrixTransformFromNode(inputParentTransformNode, rasToModel) elif inputParentTransformNode is not None: inputParentTransformNode.GetMatrixTransformToNode( self.roiModelNode.GetParentTransformNode(), rasToModel) inputIjkToRas = vtk.vtkMatrix4x4() inputOrientedImage.GetImageToWorldMatrix(inputIjkToRas) inputImage = vtk.vtkImageData() inputImage.ShallowCopy(inputOrientedImage) inputImage.SetOrigin(0, 0, 0) inputImage.SetSpacing(1, 1, 1) clippingPolyData = self.roiModelNode.GetPolyData() outputIjkToRas = vtk.vtkMatrix4x4() outputImage = vtkSegmentationCore.vtkOrientedImageData() self.clipImageWithPolyData(inputImage, outputImage, clippingPolyData, rasToModel, inputIjkToRas, outputIjkToRas, clipOutsideSurface, fillValue, reduceExtent) outputOrientedImage.DeepCopy(outputImage) outputOrientedImage.SetGeometryFromImageToWorldMatrix(outputIjkToRas)
def findLargest2DRegion(segmentationNode): qrLogic = CustomSegmentEditorLogic segmentationsLogic = slicer.modules.segmentations.logic() largestLM = None largestSize = 0 for segment in qrLogic.getAllSegments(segmentationNode): imageData = vtkSegmentationCore.vtkOrientedImageData() segmentID = segmentationNode.GetSegmentation().GetSegmentIdBySegment(segment) segmentationsLogic.GetSegmentBinaryLabelmapRepresentation(segmentationNode, segmentID, imageData) extent = imageData.GetExtent() if extent[1] != -1 and extent[3] != -1 and extent[5] != -1: tempLabel = slicer.vtkMRMLLabelMapVolumeNode() tempLabel.SetName(segment.GetName() + "CentroidHelper") segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData(imageData, tempLabel) dims = tempLabel.GetImageData().GetDimensions() size = dims[0]*dims[1] if size > largestSize: largestSize = size largestLM = tempLabel return largestLM
def convertSegmentsToSegment(self, probeNode, nodeIds): thisScene = probeNode.GetScene() #probeNode.GetSegmentation().CreateRepresentation("Binary labelmap") #added 2/22 #probeNode.GetSegmentation().SetMasterRepresentationName("Binary labelmap") #added 2/22 probeName = "ablation zone" if len(nodeIds) > 1: print( "Found multiple probe nodes, combining them into a single segment!: ", nodeIds) probeName = "combined ablation zone" segmentationNode = slicer.vtkMRMLSegmentationNode() slicer.mrmlScene.AddNode(segmentationNode) segmentationNode.CreateDefaultDisplayNodes() # only needed for display slicer.app.processEvents() for probeNodeID in nodeIds: duplicateProbeNode = thisScene.GetNodeByID(probeNodeID) #duplicateProbeNode.GetSegmentation().SetMasterRepresentationName("Binary labelmap") #ADDED 2/22 segmentType = duplicateProbeNode.GetSegmentation( ).GetMasterRepresentationName() slicer.app.processEvents() if (segmentType == "Closed surface"): mergedImage = vtk.vtkPolyData() duplicateProbeNode.GetClosedSurfaceRepresentation( duplicateProbeNode.GetSegmentation().GetNthSegmentID(0), mergedImage) newSegmentID = segmentationNode.AddSegmentFromClosedSurfaceRepresentation( mergedImage, probeNodeID, [0, 1, 0]) newSegment = segmentationNode.GetSegmentation().GetSegment( newSegmentID) newSegment.SetName(probeName) else: labelmapImage = vtkSegmentationCore.vtkOrientedImageData() duplicateProbeNode.GetBinaryLabelmapRepresentation( duplicateProbeNode.GetSegmentation().GetNthSegmentID(0), labelmapImage) newSegmentID = segmentationNode.AddSegmentFromBinaryLabelmapRepresentation( labelmapImage, probeNodeID, [1, 0, 0]) newSegment = segmentationNode.GetSegmentation().GetSegment( newSegmentID) newSegment.SetName(probeName) duplicateProbeNode.GetSegmentation().CreateRepresentation( "Closed Surface") #segmentationNode.GetSegmentation().CreateRepresentation("Closed surface") #segmentationNode.GetSegmentation().SetMasterRepresentationName("Closed surface") #slicer.mrmlScene.RemoveNode(duplicateProbeNode) duplicateProbeNode.SetDisplayVisibility(0) segmentEditorWidget = slicer.qMRMLSegmentEditorWidget() segmentEditorWidget.setMRMLScene(slicer.mrmlScene) segmentEditorNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLSegmentEditorNode") segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode) segmentEditorWidget.setSegmentationNode(segmentationNode) for i in range( 1, segmentationNode.GetSegmentation().GetNumberOfSegments() + 1): #TWO ADDITIONAL PROBES segmentEditorWidget.setActiveEffectByName("Logical operators") effect = segmentEditorWidget.activeEffect() effect.self().scriptedEffect.setParameter("Operation", "UNION") effect.self().scriptedEffect.setParameter( "ModifierSegmentID", segmentationNode.GetSegmentation().GetNthSegmentID(i)) effect.self().onApply() #segmentationNode.GetSegmentation().RemoveSegment(segmentationNode.GetSegmentation().GetNthSegmentID(i)) #for i in range(1, segmentationNode.GetSegmentation().GetNumberOfSegments()): #TWO ADDITIONAL PROBES # segmentationNode.GetSegmentation().RemoveSegment(segmentationNode.GetSegmentation().GetNthSegmentID(i)) segDisplayNode = segmentationNode.GetDisplayNode() segDisplayNode.SetOpacity(0.3) segmentationNode.GetSegmentation().CreateRepresentation( "Closed surface") segmentationNode.GetSegmentation().SetMasterRepresentationName( "Closed surface") segNum = segmentationNode.GetSegmentation().GetNumberOfSegments() for i in range(0, segNum): segmentationNode.GetSegmentation().RemoveSegment( segmentationNode.GetSegmentation().GetNthSegmentID(1)) segmentationNode.SetName("translated probe") #segmentationNode.GetSegmentation().GetNthSegment(0).SetName(probeName) return segmentationNode
def preview(self): # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() # Get segmentation segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() previewNode = self.getPreviewNode() if not previewNode or not self.mergedLabelmapGeometryImage \ or (self.clippedMasterImageDataRequired and not self.clippedMasterImageData): self.reset() # Compute merged labelmap extent (effective extent slightly expanded) 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)) return if not self.mergedLabelmapGeometryImage: self.mergedLabelmapGeometryImage = vtkSegmentationCore.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) masterImageExtent = masterImageData.GetExtent() labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent() margin = [17, 17, 17] 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]) ] self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent) previewNode = slicer.mrmlScene.CreateNodeByClass('vtkMRMLSegmentationNode') previewNode.UnRegister(None) previewNode = slicer.mrmlScene.AddNode(previewNode) 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) if self.clippedMasterImageDataRequired: self.clippedMasterImageData = vtkSegmentationCore.vtkOrientedImageData() masterImageClipper = vtk.vtkImageConstantPad() masterImageClipper.SetInputData(masterImageData) masterImageClipper.SetOutputWholeExtent(self.mergedLabelmapGeometryImage.GetExtent()) masterImageClipper.Update() self.clippedMasterImageData.ShallowCopy(masterImageClipper.GetOutput()) self.clippedMasterImageData.CopyDirections(self.mergedLabelmapGeometryImage) previewNode.SetName(segmentationNode.GetName()+" preview") mergedImage = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds) outputLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.computePreviewLabelmap(mergedImage, outputLabelmap) # Write output segmentation results in segments for index in xrange(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 = vtkSegmentationCore.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) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, previewNode, segmentID) self.updateGUIFromMRML()
def onApply(self): self.scriptedEffect.saveStateForUndo() import vtkSegmentationCorePython as vtkSegmentationCore # Get modifier labelmap and parameters operation = self.scriptedEffect.parameter("Operation") bypassMasking = (self.scriptedEffect.integerParameter("BypassMasking") != 0) selectedSegmentID = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() segmentation = segmentationNode.GetSegmentation() if operation in self.operationsRequireModifierSegment: # Get modifier segment modifierSegmentID = self.modifierSegmentID() if not modifierSegmentID: logging.error( "Operation {0} requires a selected modifier segment". format(operation)) return modifierSegment = segmentation.GetSegment(modifierSegmentID) modifierSegmentLabelmap = modifierSegment.GetRepresentation( vtkSegmentationCore.vtkSegmentationConverter. GetSegmentationBinaryLabelmapRepresentationName()) if operation == LOGICAL_COPY: if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( modifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_UNION: if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( modifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer. vtkSlicerSegmentationsModuleLogic.MODE_MERGE_MAX, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) elif operation == LOGICAL_SUBTRACT: if bypassMasking: invertedModifierSegmentLabelmap = self.getInvertedBinaryLabelmap( modifierSegmentLabelmap) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( invertedModifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer. vtkSlicerSegmentationsModuleLogic.MODE_MERGE_MIN, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect. ModificationModeRemove) elif operation == LOGICAL_INTERSECT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) intersectionLabelmap = vtkSegmentationCore.vtkOrientedImageData( ) vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage( selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap, vtkSegmentationCore. vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent()) selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent( ) modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent( ) commonExtent = [ max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]), min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]), max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]), min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]), max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]), min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5]) ] if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( intersectionLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, commonExtent) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( intersectionLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent) elif operation == LOGICAL_INVERT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) invertedSelectedSegmentLabelmap = self.getInvertedBinaryLabelmap( selectedSegmentLabelmap) if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( invertedSelectedSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( invertedSelectedSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_CLEAR or operation == LOGICAL_FILL: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) vtkSegmentationCore.vtkOrientedImageDataResample.FillImage( selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent()) if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( selectedSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( selectedSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) else: logging.error("Unknown operation: {0}".format(operation))
def processInteractionEvents(self, callerInteractor, eventId, viewWidget): import vtkSegmentationCorePython as vtkSegmentationCore abortEvent = False # Only allow in modes where segment selection is needed if not self.currentOperationRequiresSegmentSelection(): return False # Only allow for slice views if viewWidget.className() != "qMRMLSliceWidget": return abortEvent if eventId != vtk.vtkCommand.LeftButtonPressEvent: return abortEvent abortEvent = True # Generate merged labelmap of all visible segments segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() visibleSegmentIds = vtk.vtkStringArray() segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds) if visibleSegmentIds.GetNumberOfValues() == 0: logging.info("Smoothing operation skipped: there are no visible segments") return abortEvent self.scriptedEffect.saveStateForUndo() # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) operationName = self.scriptedEffect.parameter("Operation") if operationName == ADD_SELECTED_ISLAND: inputLabelImage = vtkSegmentationCore.vtkOrientedImageData() if not segmentationNode.GenerateMergedLabelmapForAllSegments(inputLabelImage, vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED, None, visibleSegmentIds): logging.error('Failed to apply smoothing: cannot get list of visible segments') qt.QApplication.restoreOverrideCursor() return abortEvent else: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) thresh.Update() # Create oriented image data from output import vtkSegmentationCorePython as vtkSegmentationCore inputLabelImage = vtkSegmentationCore.vtkOrientedImageData() inputLabelImage.ShallowCopy(thresh.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) inputLabelImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) xy = callerInteractor.GetEventPosition() ijk = self.xyToIjk(xy, viewWidget, inputLabelImage) pixelValue = inputLabelImage.GetScalarComponentAsFloat(ijk[0], ijk[1], ijk[2], 0) try: floodFillingFilter = vtk.vtkImageThresholdConnectivity() floodFillingFilter.SetInputData(inputLabelImage) seedPoints = vtk.vtkPoints() origin = inputLabelImage.GetOrigin() spacing = inputLabelImage.GetSpacing() seedPoints.InsertNextPoint(origin[0]+ijk[0]*spacing[0], origin[1]+ijk[1]*spacing[1], origin[2]+ijk[2]*spacing[2]) floodFillingFilter.SetSeedPoints(seedPoints) floodFillingFilter.ThresholdBetween(pixelValue, pixelValue) if operationName == ADD_SELECTED_ISLAND: floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) floodFillingFilter.Update() modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput()) self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) elif pixelValue != 0: # if clicked on empty part then there is nothing to remove or keep if operationName == KEEP_SELECTED_ISLAND: floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) else: # operationName == REMOVE_SELECTED_ISLAND: floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) floodFillingFilter.Update() modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput()) if operationName == KEEP_SELECTED_ISLAND: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) else: # operationName == REMOVE_SELECTED_ISLAND: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove) except IndexError: logging.error('apply: Failed to threshold master volume!') finally: qt.QApplication.restoreOverrideCursor() return abortEvent
def growCut(self): # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() # Get segmentation 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 = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS, 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 import vtkITK 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 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(slicer.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.CopyDirections(mergedImage) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, segmentationNode, segmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, newSegmentLabelmap.GetExtent())
def growCut(self): # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() # Get segmentation 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 = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments( mergedImage, vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS, 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 import vtkITK 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 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( slicer.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.CopyDirections(mergedImage) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( newSegmentLabelmap, segmentationNode, segmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, newSegmentLabelmap.GetExtent())
def TestSection_2_qMRMLSegmentationGeometryWidget(self): logging.info('Test section 2: qMRMLSegmentationGeometryWidget') import vtkSegmentationCore binaryLabelmapReprName = vtkSegmentationCore.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName() closedSurfaceReprName = vtkSegmentationCore.vtkSegmentationConverter.GetClosedSurfaceRepresentationName() # Use MRHead and Tinypatient for testing import SampleData sampleDataLogic = SampleData.SampleDataLogic() mrVolumeNode = sampleDataLogic.downloadMRHead() [tinyVolumeNode, tinySegmentationNode] = sampleDataLogic.downloadSample('TinyPatient') # Convert MRHead to oriented image data import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic mrOrientedImageData = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.CreateOrientedImageDataFromVolumeNode(mrVolumeNode) mrOrientedImageData.UnRegister(None) # Create segmentation node with binary labelmap master and one segment with MRHead geometry segmentationNode = slicer.vtkMRMLSegmentationNode() segmentationNode.GetSegmentation().SetMasterRepresentationName(binaryLabelmapReprName) geometryStr = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(mrOrientedImageData) segmentationNode.GetSegmentation().SetConversionParameter( vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryStr) slicer.mrmlScene.AddNode(segmentationNode) threshold = vtk.vtkImageThreshold() threshold.SetInputData(mrOrientedImageData) threshold.ThresholdByUpper(16.0) threshold.SetInValue(1) threshold.SetOutValue(0) threshold.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) threshold.Update() segmentOrientedImageData = vtkSegmentationCore.vtkOrientedImageData() segmentOrientedImageData.DeepCopy(threshold.GetOutput()) mrImageToWorldMatrix = vtk.vtkMatrix4x4() mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix) segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix) segment = vtkSegmentationCore.vtkSegment() segment.SetName('Brain') segment.SetColor(0.0,0.0,1.0) segment.AddRepresentation(binaryLabelmapReprName, segmentOrientedImageData) segmentationNode.GetSegmentation().AddSegment(segment) # Create geometry widget geometryWidget = slicer.qMRMLSegmentationGeometryWidget() geometryWidget.setSegmentationNode(segmentationNode) geometryWidget.editEnabled = True geometryImageData = vtkSegmentationCore.vtkOrientedImageData() # To contain the output later # Volume source with no transforms geometryWidget.setSourceNode(tinyVolumeNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (49,49,23), (248.8439, 248.2890, -123.75), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 92) # Transformed volume source translationTransformMatrix = vtk.vtkMatrix4x4() translationTransformMatrix.SetElement(0,3,24.5) translationTransformMatrix.SetElement(1,3,24.5) translationTransformMatrix.SetElement(2,3,11.5) translationTransformNode = slicer.vtkMRMLLinearTransformNode() translationTransformNode.SetName('TestTranslation') slicer.mrmlScene.AddNode(translationTransformNode) translationTransformNode.SetMatrixTransformToParent(translationTransformMatrix) tinyVolumeNode.SetAndObserveTransformNodeID(translationTransformNode.GetID()) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (49,49,23), (224.3439, 223.7890, -135.25), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 94) # Volume source with isotropic spacing tinyVolumeNode.SetAndObserveTransformNodeID(None) geometryWidget.setIsotropicSpacing(True) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (23,23,23), (248.8439, 248.2890, -123.75), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 414) # Volume source with oversampling geometryWidget.setIsotropicSpacing(False) geometryWidget.setOversamplingFactor(2.0) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (24.5, 24.5, 11.5), (261.0939, 260.5390, -129.5), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 751) slicer.util.delayDisplay('Volume source cases - OK') # Segmentation source with binary labelmap master geometryWidget.setOversamplingFactor(1.0) geometryWidget.setSourceNode(tinySegmentationNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (49,49,23), (248.8439, 248.2890, -123.75), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 92) # Segmentation source with closed surface master tinySegmentationNode.GetSegmentation().SetConversionParameter('Smoothing factor', '0.0') self.assertTrue(tinySegmentationNode.GetSegmentation().CreateRepresentation(closedSurfaceReprName)) tinySegmentationNode.GetSegmentation().SetMasterRepresentationName(closedSurfaceReprName) tinySegmentationNode.Modified() # Trigger re-calculation of geometry (only generic Modified event is observed) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (1,1,1), (-167.156, -217.711, -135.75), [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846) slicer.util.delayDisplay('Segmentation source cases - OK') # Model source with no transform outputModelHierarchy = slicer.vtkMRMLModelHierarchyNode() slicer.mrmlScene.AddNode(outputModelHierarchy) success = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ExportVisibleSegmentsToModelHierarchy( tinySegmentationNode, outputModelHierarchy ) self.assertTrue(success) modelNode = slicer.util.getNode('Body_Contour') geometryWidget.setSourceNode(modelNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (1,1,1), (-167.156, -217.711, -135.75), [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846) # Transformed model source rotationTransform = vtk.vtkTransform() rotationTransform.RotateX(45) rotationTransformMatrix = vtk.vtkMatrix4x4() rotationTransform.GetMatrix(rotationTransformMatrix) rotationTransformNode = slicer.vtkMRMLLinearTransformNode() rotationTransformNode.SetName('TestRotation') slicer.mrmlScene.AddNode(rotationTransformNode) rotationTransformNode.SetMatrixTransformToParent(rotationTransformMatrix) modelNode.SetAndObserveTransformNodeID(rotationTransformNode.GetID()) modelNode.Modified() geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (1,1,1), (-167.156, -58.6623, -249.228), [[0.0, 0.0, 1.0], [-0.7071, -0.7071, 0.0], [0.7071, -0.7071, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5221241) # ROI source roiNode = slicer.vtkMRMLAnnotationROINode() roiNode.SetName('SourceROI') slicer.mrmlScene.AddNode(roiNode) roiNode.UnRegister(None) xyz = [0]*3 center = [0]*3 slicer.vtkMRMLSliceLogic.GetVolumeRASBox(tinyVolumeNode, xyz, center) radius = map(lambda x: x/2.0, xyz) roiNode.SetXYZ(center) roiNode.SetRadiusXYZ(radius) geometryWidget.setSourceNode(roiNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (1,1,1), (-216.156, -217.711, -135.75), [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846) slicer.util.delayDisplay('Model and ROI source cases - OK') slicer.util.delayDisplay('Segmentation geometry widget test passed')
def processInteractionEvents(self, callerInteractor, eventId, viewWidget): import vtkSegmentationCorePython as vtkSegmentationCore abortEvent = False # Only allow in modes where segment selection is needed if not self.currentOperationRequiresSegmentSelection(): return False # Only allow for slice views if viewWidget.className() != "qMRMLSliceWidget": return abortEvent if eventId != vtk.vtkCommand.LeftButtonPressEvent: return abortEvent abortEvent = True # Generate merged labelmap of all visible segments segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() visibleSegmentIds = vtk.vtkStringArray() segmentationNode.GetDisplayNode().GetVisibleSegmentIDs( visibleSegmentIds) if visibleSegmentIds.GetNumberOfValues() == 0: logging.info( "Smoothing operation skipped: there are no visible segments") return abortEvent self.scriptedEffect.saveStateForUndo() # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) operationName = self.scriptedEffect.parameter("Operation") if operationName == ADD_SELECTED_ISLAND: inputLabelImage = vtkSegmentationCore.vtkOrientedImageData() if not segmentationNode.GenerateMergedLabelmapForAllSegments( inputLabelImage, vtkSegmentationCore.vtkSegmentation. EXTENT_UNION_OF_SEGMENTS_PADDED, None, visibleSegmentIds): logging.error( 'Failed to apply smoothing: cannot get list of visible segments' ) qt.QApplication.restoreOverrideCursor() return abortEvent else: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) thresh.Update() # Create oriented image data from output import vtkSegmentationCorePython as vtkSegmentationCore inputLabelImage = vtkSegmentationCore.vtkOrientedImageData() inputLabelImage.ShallowCopy(thresh.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) inputLabelImage.SetImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) xy = callerInteractor.GetEventPosition() ijk = self.xyToIjk(xy, viewWidget, inputLabelImage) pixelValue = inputLabelImage.GetScalarComponentAsFloat( ijk[0], ijk[1], ijk[2], 0) try: floodFillingFilter = vtk.vtkImageThresholdConnectivity() floodFillingFilter.SetInputData(inputLabelImage) seedPoints = vtk.vtkPoints() origin = inputLabelImage.GetOrigin() spacing = inputLabelImage.GetSpacing() seedPoints.InsertNextPoint(origin[0] + ijk[0] * spacing[0], origin[1] + ijk[1] * spacing[1], origin[2] + ijk[2] * spacing[2]) floodFillingFilter.SetSeedPoints(seedPoints) floodFillingFilter.ThresholdBetween(pixelValue, pixelValue) if operationName == ADD_SELECTED_ISLAND: floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) floodFillingFilter.Update() modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap( ) modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput()) self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) elif pixelValue != 0: # if clicked on empty part then there is nothing to remove or keep if operationName == KEEP_SELECTED_ISLAND: floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) else: # operationName == REMOVE_SELECTED_ISLAND: floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) floodFillingFilter.Update() modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap( ) modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput()) if operationName == KEEP_SELECTED_ISLAND: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) else: # operationName == REMOVE_SELECTED_ISLAND: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect. ModificationModeRemove) except IndexError: logging.error('apply: Failed to threshold master volume!') finally: qt.QApplication.restoreOverrideCursor() return abortEvent
def onApply(self): import vtkSegmentationCorePython as vtkSegmentationCore # Get modifier labelmap and parameters operation = self.scriptedEffect.parameter("Operation") bypassMasking = (self.scriptedEffect.integerParameter("BypassMasking") != 0) selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() segmentation = segmentationNode.GetSegmentation() if operation in self.operationsRequireModifierSegment: # Get modifier segment modifierSegmentID = self.modifierSegmentID() if not modifierSegmentID: logging.error("Operation {0} requires a selected modifier segment".format(operation)) return modifierSegment = segmentation.GetSegment(modifierSegmentID) modifierSegmentLabelmap = modifierSegment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) if operation == LOGICAL_COPY: if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(modifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_UNION: if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(modifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_MERGE_MAX, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) elif operation == LOGICAL_SUBTRACT: if bypassMasking: invertedModifierSegmentLabelmap = self.getInvertedBinaryLabelmap(modifierSegmentLabelmap) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(invertedModifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_MERGE_MIN, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove) elif operation == LOGICAL_INTERSECT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() intersectionLabelmap = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage(selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap, vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent()) selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent() modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent() commonExtent = [max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]), min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]), max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]), min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]), max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]), min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5])] if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(intersectionLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, commonExtent) else: self.scriptedEffect.modifySelectedSegmentByLabelmap(intersectionLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent) elif operation == LOGICAL_INVERT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() invertedSelectedSegmentLabelmap = self.getInvertedBinaryLabelmap(selectedSegmentLabelmap) if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( invertedSelectedSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE) else: self.scriptedEffect.modifySelectedSegmentByLabelmap(invertedSelectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_CLEAR or operation == LOGICAL_FILL: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent()) if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( selectedSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE) else: self.scriptedEffect.modifySelectedSegmentByLabelmap(selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) else: logging.error("Uknown operation: {0}".format(operation))
def addGrayscaleVolumeStatistics(self): import vtkSegmentationCorePython as vtkSegmentationCore containsLabelmapRepresentation = self.segmentationNode.GetSegmentation().ContainsRepresentation( vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) if not containsLabelmapRepresentation: return if self.grayscaleNode is None or self.grayscaleNode.GetImageData() is None: return # Get geometry of grayscale volume node as oriented image data referenceGeometry_Reference = vtkSegmentationCore.vtkOrientedImageData() # reference geometry in reference node coordinate system referenceGeometry_Reference.SetExtent(self.grayscaleNode.GetImageData().GetExtent()) ijkToRasMatrix = vtk.vtkMatrix4x4() self.grayscaleNode.GetIJKToRASMatrix(ijkToRasMatrix) referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix(ijkToRasMatrix) # Get transform between grayscale volume and segmentation segmentationToReferenceGeometryTransform = vtk.vtkGeneralTransform() slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(self.segmentationNode.GetParentTransformNode(), self.grayscaleNode.GetParentTransformNode(), segmentationToReferenceGeometryTransform) cubicMMPerVoxel = reduce(lambda x,y: x*y, referenceGeometry_Reference.GetSpacing()) ccPerCubicMM = 0.001 for segmentID in self.statistics["SegmentIDs"]: segment = self.segmentationNode.GetSegmentation().GetSegment(segmentID) segmentLabelmap = segment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) segmentLabelmap_Reference = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentLabelmap, referenceGeometry_Reference, segmentLabelmap_Reference, False, # nearest neighbor interpolation False, # no padding segmentationToReferenceGeometryTransform) # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(segmentLabelmap_Reference) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) thresh.Update() # Use binary labelmap as a stencil stencil = vtk.vtkImageToImageStencil() stencil.SetInputData(thresh.GetOutput()) stencil.ThresholdByUpper(labelValue) stencil.Update() stat = vtk.vtkImageAccumulate() stat.SetInputData(self.grayscaleNode.GetImageData()) stat.SetStencilData(stencil.GetOutput()) stat.Update() # Add data to statistics list self.statistics[segmentID,"GS voxel count"] = stat.GetVoxelCount() self.statistics[segmentID,"GS volume mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel self.statistics[segmentID,"GS volume cc"] = stat.GetVoxelCount() * cubicMMPerVoxel * ccPerCubicMM if stat.GetVoxelCount()>0: self.statistics[segmentID,"GS min"] = stat.GetMin()[0] self.statistics[segmentID,"GS max"] = stat.GetMax()[0] self.statistics[segmentID,"GS mean"] = stat.GetMean()[0] self.statistics[segmentID,"GS stdev"] = stat.GetStandardDeviation()[0]
def TestSection_1_AddRemoveSegment(self): # Add/remove segment from segmentation (check display properties, color table, etc.) logging.info('Test section 1: 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('Body_Contour').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('Tumor_Contour').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(50) 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() self.assertEqual(imageStat.GetVoxelCount(), 1000) imageStatResult = imageStat.GetOutput() for i in range(4): logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0))) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 795) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 194) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 4) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 7) # 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() self.assertEqual(imageStat.GetVoxelCount(), 1000) imageStatResult = imageStat.GetOutput() for i in range(4): logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0))) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 795) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 194) self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 6) 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 onApply(self): import vtkSegmentationCorePython as vtkSegmentationCore # Allow users revert to this state by clicking Undo self.scriptedEffect.saveStateForUndo() # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) if self.segmentMarkupNode and ( self.segmentModel.GetPolyData().GetNumberOfPolys() > 0): self.observeSegmentation(False) operationName = self.scriptedEffect.parameter("Operation") modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() WorldToModifierLabelmapIjkTransform = vtk.vtkTransform() WorldToModifierLabelmapIjkTransformer = vtk.vtkTransformPolyDataFilter( ) WorldToModifierLabelmapIjkTransformer.SetTransform( WorldToModifierLabelmapIjkTransform) WorldToModifierLabelmapIjkTransformer.SetInputConnection( self.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()) if operationName in ("FILL_INSIDE", "ERASE_INSIDE", "SET"): stencilToImage.SetInsideValue(1.0) stencilToImage.SetOutsideValue(0.0) else: stencilToImage.SetInsideValue(0.0) stencilToImage.SetOutsideValue(1.0) stencilToImage.SetOutputScalarType( modifierLabelmap.GetScalarType()) stencilPositioner = vtk.vtkImageChangeInformation() stencilPositioner.SetInputConnection( stencilToImage.GetOutputPort()) stencilPositioner.SetOutputSpacing(modifierLabelmap.GetSpacing()) stencilPositioner.SetOutputOrigin(modifierLabelmap.GetOrigin()) stencilPositioner.Update() orientedStencilPositionerOuput = vtkSegmentationCore.vtkOrientedImageData( ) orientedStencilPositionerOuput.ShallowCopy( stencilToImage.GetOutput()) imageToWorld = vtk.vtkMatrix4x4() modifierLabelmap.GetImageToWorldMatrix(imageToWorld) orientedStencilPositionerOuput.SetImageToWorldMatrix(imageToWorld) vtkSegmentationCore.vtkOrientedImageDataResample.ModifyImage( modifierLabelmap, orientedStencilPositionerOuput, vtkSegmentationCore.vtkOrientedImageDataResample. OPERATION_MAXIMUM) modMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd if operationName == "ERASE_INSIDE" or operationName == "ERASE_OUTSIDE": modMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove elif operationName == "SET": modMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, modMode) import numpy n = self.segmentMarkupNode.GetNumberOfFiducials() # get fiducial positions fPos = numpy.zeros((n, 3)) for i in xrange(n): coord = [0.0, 0.0, 0.0] self.segmentMarkupNode.GetNthFiducialPosition(i, coord) fPos[i] = coord segmentID = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() segment = segmentationNode.GetSegmentation().GetSegment(segmentID) segment.SetTag("fP", fPos.tostring()) segment.SetTag("fN", n) self.reset() self.createNewMarkupNode() self.fiducialPlacementToggle.setCurrentNode(self.segmentMarkupNode) self.observeSegmentation(True) qt.QApplication.restoreOverrideCursor()
def smoothMultipleSegments(self): import vtkSegmentationCorePython as vtkSegmentationCore # Generate merged labelmap of all visible segments segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() visibleSegmentIds = vtk.vtkStringArray() segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds) if visibleSegmentIds.GetNumberOfValues() == 0: logging.info("Smoothing operation skipped: there are no visible segments") return mergedImage = vtkSegmentationCore.vtkOrientedImageData() if not segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED, None, visibleSegmentIds): logging.error('Failed to apply smoothing: cannot get list of visible segments') return segmentLabelValues = [] # list of [segmentId, labelValue] for i in range(visibleSegmentIds.GetNumberOfValues()): segmentId = visibleSegmentIds.GetValue(i) segmentLabelValues.append([segmentId, i+1]) # Perform smoothing in voxel space ici = vtk.vtkImageChangeInformation() ici.SetInputData(mergedImage) ici.SetOutputSpacing(1, 1, 1) ici.SetOutputOrigin(0, 0, 0) # Convert labelmap to combined polydata # vtkDiscreteFlyingEdges3D cannot be used here, as in the output of that filter, # each labeled region is completely disconnected from neighboring regions, and # for joint smoothing it is essential for the points to move together. convertToPolyData = vtk.vtkDiscreteMarchingCubes() convertToPolyData.SetInputConnection(ici.GetOutputPort()) convertToPolyData.SetNumberOfContours(len(segmentLabelValues)) contourIndex = 0 for segmentId, labelValue in segmentLabelValues: convertToPolyData.SetValue(contourIndex, labelValue) contourIndex += 1 # Low-pass filtering using Taubin's method smoothingFactor = self.scriptedEffect.doubleParameter("JointTaubinSmoothingFactor") smoothingIterations = 100 # according to VTK documentation 10-20 iterations could be enough but we use a higher value to reduce chance of shrinking passBand = pow(10.0, -4.0*smoothingFactor) # gives a nice range of 1-0.0001 from a user input of 0-1 smoother = vtk.vtkWindowedSincPolyDataFilter() smoother.SetInputConnection(convertToPolyData.GetOutputPort()) smoother.SetNumberOfIterations(smoothingIterations) smoother.BoundarySmoothingOff() smoother.FeatureEdgeSmoothingOff() smoother.SetFeatureAngle(90.0) smoother.SetPassBand(passBand) smoother.NonManifoldSmoothingOn() smoother.NormalizeCoordinatesOn() # Extract a label threshold = vtk.vtkThreshold() threshold.SetInputConnection(smoother.GetOutputPort()) # Convert to polydata geometryFilter = vtk.vtkGeometryFilter() geometryFilter.SetInputConnection(threshold.GetOutputPort()) # Convert polydata to stencil polyDataToImageStencil = vtk.vtkPolyDataToImageStencil() polyDataToImageStencil.SetInputConnection(geometryFilter.GetOutputPort()) polyDataToImageStencil.SetOutputSpacing(1,1,1) polyDataToImageStencil.SetOutputOrigin(0,0,0) polyDataToImageStencil.SetOutputWholeExtent(mergedImage.GetExtent()) # Convert stencil to image stencil = vtk.vtkImageStencil() emptyBinaryLabelMap = vtk.vtkImageData() emptyBinaryLabelMap.SetExtent(mergedImage.GetExtent()) emptyBinaryLabelMap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1) vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(emptyBinaryLabelMap, 0) stencil.SetInputData(emptyBinaryLabelMap) stencil.SetStencilConnection(polyDataToImageStencil.GetOutputPort()) stencil.ReverseStencilOn() stencil.SetBackgroundValue(1) # General foreground value is 1 (background value because of reverse stencil) imageToWorldMatrix = vtk.vtkMatrix4x4() mergedImage.GetImageToWorldMatrix(imageToWorldMatrix) for segmentId, labelValue in segmentLabelValues: threshold.ThresholdBetween(labelValue, labelValue) stencil.Update() smoothedBinaryLabelMap = vtkSegmentationCore.vtkOrientedImageData() smoothedBinaryLabelMap.ShallowCopy(stencil.GetOutput()) smoothedBinaryLabelMap.SetImageToWorldMatrix(imageToWorldMatrix) # Write results to segments directly, bypassing masking slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(smoothedBinaryLabelMap, segmentationNode, segmentId, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, smoothedBinaryLabelMap.GetExtent())
def jumpToSegmentAndCreateScreenShot(segmentationNode, segment, widgets, center=False, crosshair=False): imageData = vtkSegmentationCore.vtkOrientedImageData() segmentID = segmentationNode.GetSegmentation().GetSegmentIdBySegment( segment) segmentationsLogic = slicer.modules.segmentations.logic() segmentationsLogic.GetSegmentBinaryLabelmapRepresentation( segmentationNode, segmentID, imageData) extent = imageData.GetExtent() if extent[1] != -1 and extent[3] != -1 and extent[5] != -1: tempLabel = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(tempLabel) tempLabel.SetName(segment.GetName() + "CentroidHelper") segmentationsLogic.CreateLabelmapVolumeFromOrientedImageData( imageData, tempLabel) CustomSegmentEditorLogic.applyThreshold(tempLabel, 1) centroid = ModuleLogicMixin.getCentroidForLabel(tempLabel, 1) slicer.mrmlScene.RemoveNode(tempLabel) annotationNodes = [] crosshairButton = None if crosshair: crosshairButton = CrosshairButton() crosshairButton.setSliceIntersectionEnabled(True) crosshairButton.checked = True for widget in widgets: sliceLogic = widget.sliceLogic() sliceNode = sliceLogic.GetSliceNode() if not center: sliceNode.JumpSliceByOffsetting(centroid[0], centroid[1], centroid[2]) else: markupsLogic = slicer.modules.markups.logic() markupsLogic.JumpSlicesToLocation(centroid[0], centroid[1], centroid[2], True) dNodeProperties = ScreenShotHelper.saveSegmentDisplayProperties( segmentationNode, segment) segmentationNode.GetDisplayNode().SetAllSegmentsVisibility( False) ScreenShotHelper.setDisplayNodeProperties(segmentationNode, segment, properties={ 'fill': True, 'outline': True, 'visible': True }) if crosshairButton: crosshairButton.crosshairNode.SetCrosshairRAS(centroid) annotationNode = ScreenShotHelper.takeScreenShot( "{}_Screenshot_{}_{}".format(segment.GetName(), sliceNode.GetName(), sliceNode.GetOrientation()), "", widget) segmentationNode.GetDisplayNode().SetAllSegmentsVisibility( True) ScreenShotHelper.setDisplayNodeProperties( segmentationNode, segment, dNodeProperties) annotationNodes.append(annotationNode) if crosshairButton: crosshairButton.checked = False return annotationNodes[0] if len( annotationNodes) == 1 else annotationNodes
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. """ 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( ) 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 convert 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 += ";AnatomicRegionModifier:" + 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 processInteractionEvents(self, callerInteractor, eventId, viewWidget): abortEvent = False # Only allow for slice views if viewWidget.className() != "qMRMLSliceWidget": return abortEvent if eventId == vtk.vtkCommand.LeftButtonPressEvent: self.scriptedEffect.saveStateForUndo() # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() # Get modifier labelmap modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() xy = callerInteractor.GetEventPosition() ijk = self.xyToIjk(xy, viewWidget, masterImageData) pixelValue = masterImageData.GetScalarComponentAsFloat(ijk[0], ijk[1], ijk[2], 0) useSegmentationAsStencil = False try: # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) # Perform thresholding floodFillingFilter = vtk.vtkImageThresholdConnectivity() floodFillingFilter.SetInputData(masterImageData) seedPoints = vtk.vtkPoints() origin = masterImageData.GetOrigin() spacing = masterImageData.GetSpacing() seedPoints.InsertNextPoint(origin[0]+ijk[0]*spacing[0], origin[1]+ijk[1]*spacing[1], origin[2]+ijk[2]*spacing[2]) floodFillingFilter.SetSeedPoints(seedPoints) maskImageData = vtkSegmentationCore.vtkOrientedImageData() intensityBasedMasking = self.scriptedEffect.parameterSetNode().GetMasterVolumeIntensityMask() segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() success = segmentationNode.GenerateEditMask(maskImageData, self.scriptedEffect.parameterSetNode().GetMaskMode(), masterImageData, # reference geometry self.scriptedEffect.parameterSetNode().GetSelectedSegmentID(), self.scriptedEffect.parameterSetNode().GetMaskSegmentID() if self.scriptedEffect.parameterSetNode().GetMaskSegmentID() else "", masterImageData if intensityBasedMasking else None, self.scriptedEffect.parameterSetNode().GetMasterVolumeIntensityMaskRange() if intensityBasedMasking else None) if success: stencil = vtk.vtkImageToImageStencil() stencil.SetInputData(maskImageData) stencil.ThresholdByLower(0) stencil.Update() floodFillingFilter.SetStencilData(stencil.GetOutput()) else: logging.error("Failed to create edit mask") neighborhoodSizeMm = self.neighborhoodSizeMmSlider.value floodFillingFilter.SetNeighborhoodRadius(neighborhoodSizeMm,neighborhoodSizeMm,neighborhoodSizeMm) floodFillingFilter.SetNeighborhoodFraction(0.5) if useSegmentationAsStencil: stencilFilter = vtk.vtkImageToImageStencil() stencilFilter.SetInputData(selectedSegmentLabelmap) stencilFilter.ThresholdByLower(0) stencilFilter.Update() floodFillingFilter.SetStencilData(stencilFilter.GetOutput()) pixelValueTolerance = float(self.intensityToleranceSlider.value) floodFillingFilter.ThresholdBetween(pixelValue-pixelValueTolerance, pixelValue+pixelValueTolerance) floodFillingFilter.SetInValue(1) floodFillingFilter.SetOutValue(0) floodFillingFilter.Update() modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput()) except IndexError: logging.error('apply: Failed to threshold master volume!') finally: qt.QApplication.restoreOverrideCursor() # Apply changes self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) abortEvent = True return abortEvent
def computeStatistics(self, segmentID): import vtkSegmentationCorePython as vtkSegmentationCore requestedKeys = self.getRequestedKeys() segmentationNode = slicer.mrmlScene.GetNodeByID(self.getParameterNode().GetParameter("Segmentation")) grayscaleNode = slicer.mrmlScene.GetNodeByID(self.getParameterNode().GetParameter("ScalarVolume")) if len(requestedKeys)==0: return {} containsLabelmapRepresentation = segmentationNode.GetSegmentation().ContainsRepresentation( vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) if not containsLabelmapRepresentation: return {} if grayscaleNode is None or grayscaleNode.GetImageData() is None: return {} # Get geometry of grayscale volume node as oriented image data # reference geometry in reference node coordinate system referenceGeometry_Reference = vtkSegmentationCore.vtkOrientedImageData() referenceGeometry_Reference.SetExtent(grayscaleNode.GetImageData().GetExtent()) ijkToRasMatrix = vtk.vtkMatrix4x4() grayscaleNode.GetIJKToRASMatrix(ijkToRasMatrix) referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix(ijkToRasMatrix) # Get transform between grayscale volume and segmentation segmentationToReferenceGeometryTransform = vtk.vtkGeneralTransform() slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(segmentationNode.GetParentTransformNode(), grayscaleNode.GetParentTransformNode(), segmentationToReferenceGeometryTransform) cubicMMPerVoxel = reduce(lambda x,y: x*y, referenceGeometry_Reference.GetSpacing()) ccPerCubicMM = 0.001 segment = segmentationNode.GetSegmentation().GetSegment(segmentID) segBinaryLabelName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName() segmentLabelmap = segment.GetRepresentation(segBinaryLabelName) segmentLabelmap_Reference = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentLabelmap, referenceGeometry_Reference, segmentLabelmap_Reference, False, # nearest neighbor interpolation False, # no padding segmentationToReferenceGeometryTransform) # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(segmentLabelmap_Reference) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) thresh.Update() # Use binary labelmap as a stencil stencil = vtk.vtkImageToImageStencil() stencil.SetInputData(thresh.GetOutput()) stencil.ThresholdByUpper(labelValue) stencil.Update() stat = vtk.vtkImageAccumulate() stat.SetInputData(grayscaleNode.GetImageData()) stat.SetStencilData(stencil.GetOutput()) stat.Update() # create statistics list stats = {} if "voxel_count" in requestedKeys: stats["voxel_count"] = stat.GetVoxelCount() if "volume_mm3" in requestedKeys: stats["volume_mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel if "volume_cm3" in requestedKeys: stats["volume_cm3"] = stat.GetVoxelCount() * cubicMMPerVoxel * ccPerCubicMM if stat.GetVoxelCount()>0: if "min" in requestedKeys: stats["min"] = stat.GetMin()[0] if "max" in requestedKeys: stats["max"] = stat.GetMax()[0] if "mean" in requestedKeys: stats["mean"] = stat.GetMean()[0] if "stdev" in requestedKeys: stats["stdev"] = stat.GetStandardDeviation()[0] return stats
def TestSection_03_qMRMLSegmentationGeometryWidget(self): logging.info('Test section 2: qMRMLSegmentationGeometryWidget') import vtkSegmentationCore binaryLabelmapReprName = vtkSegmentationCore.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName( ) closedSurfaceReprName = vtkSegmentationCore.vtkSegmentationConverter.GetClosedSurfaceRepresentationName( ) # Use MRHead and Tinypatient for testing import SampleData mrVolumeNode = SampleData.downloadSample("MRHead") [tinyVolumeNode, tinySegmentationNode] = SampleData.downloadSamples('TinyPatient') # Convert MRHead to oriented image data import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic mrOrientedImageData = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.CreateOrientedImageDataFromVolumeNode( mrVolumeNode) mrOrientedImageData.UnRegister(None) # Create segmentation node with binary labelmap master and one segment with MRHead geometry segmentationNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLSegmentationNode') segmentationNode.GetSegmentation().SetMasterRepresentationName( binaryLabelmapReprName) geometryStr = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry( mrOrientedImageData) segmentationNode.GetSegmentation().SetConversionParameter( vtkSegmentationCore.vtkSegmentationConverter. GetReferenceImageGeometryParameterName(), geometryStr) threshold = vtk.vtkImageThreshold() threshold.SetInputData(mrOrientedImageData) threshold.ThresholdByUpper(16.0) threshold.SetInValue(1) threshold.SetOutValue(0) threshold.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) threshold.Update() segmentOrientedImageData = vtkSegmentationCore.vtkOrientedImageData() segmentOrientedImageData.DeepCopy(threshold.GetOutput()) mrImageToWorldMatrix = vtk.vtkMatrix4x4() mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix) segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix) segment = vtkSegmentationCore.vtkSegment() segment.SetName('Brain') segment.SetColor(0.0, 0.0, 1.0) segment.AddRepresentation(binaryLabelmapReprName, segmentOrientedImageData) segmentationNode.GetSegmentation().AddSegment(segment) # Create geometry widget geometryWidget = slicer.qMRMLSegmentationGeometryWidget() geometryWidget.setSegmentationNode(segmentationNode) geometryWidget.editEnabled = True geometryImageData = vtkSegmentationCore.vtkOrientedImageData( ) # To contain the output later # Volume source with no transforms geometryWidget.setSourceNode(tinyVolumeNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (49, 49, 23), (248.8439, 248.2890, -123.75), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 92) # Transformed volume source translationTransformMatrix = vtk.vtkMatrix4x4() translationTransformMatrix.SetElement(0, 3, 24.5) translationTransformMatrix.SetElement(1, 3, 24.5) translationTransformMatrix.SetElement(2, 3, 11.5) translationTransformNode = slicer.vtkMRMLLinearTransformNode() translationTransformNode.SetName('TestTranslation') slicer.mrmlScene.AddNode(translationTransformNode) translationTransformNode.SetMatrixTransformToParent( translationTransformMatrix) tinyVolumeNode.SetAndObserveTransformNodeID( translationTransformNode.GetID()) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (49, 49, 23), (224.3439, 223.7890, -135.25), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 94) # Volume source with isotropic spacing tinyVolumeNode.SetAndObserveTransformNodeID(None) geometryWidget.setIsotropicSpacing(True) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (23, 23, 23), (248.8439, 248.2890, -123.75), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 414) # Volume source with oversampling geometryWidget.setIsotropicSpacing(False) geometryWidget.setOversamplingFactor(2.0) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (24.5, 24.5, 11.5), (261.0939, 260.5390, -129.5), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 751) slicer.util.delayDisplay('Volume source cases - OK') # Segmentation source with binary labelmap master geometryWidget.setOversamplingFactor(1.0) geometryWidget.setSourceNode(tinySegmentationNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (49, 49, 23), (248.8439, 248.2890, -123.75), [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 92) # Segmentation source with closed surface master tinySegmentationNode.GetSegmentation().SetConversionParameter( 'Smoothing factor', '0.0') self.assertTrue( tinySegmentationNode.GetSegmentation().CreateRepresentation( closedSurfaceReprName)) tinySegmentationNode.GetSegmentation().SetMasterRepresentationName( closedSurfaceReprName) tinySegmentationNode.Modified( ) # Trigger re-calculation of geometry (only generic Modified event is observed) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (1, 1, 1), (-167.156, -217.711, -135.75), [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846) slicer.util.delayDisplay('Segmentation source cases - OK') # Model source with no transform shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode( slicer.mrmlScene) outputFolderId = shNode.CreateFolderItem(shNode.GetSceneItemID(), 'ModelsFolder') success = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ExportVisibleSegmentsToModels( tinySegmentationNode, outputFolderId) self.assertTrue(success) modelNode = slicer.util.getNode('Body_Contour') geometryWidget.setSourceNode(modelNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (1, 1, 1), (-167.156, -217.711, -135.75), [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846) # Transformed model source rotationTransform = vtk.vtkTransform() rotationTransform.RotateX(45) rotationTransformMatrix = vtk.vtkMatrix4x4() rotationTransform.GetMatrix(rotationTransformMatrix) rotationTransformNode = slicer.vtkMRMLLinearTransformNode() rotationTransformNode.SetName('TestRotation') slicer.mrmlScene.AddNode(rotationTransformNode) rotationTransformNode.SetMatrixTransformToParent( rotationTransformMatrix) modelNode.SetAndObserveTransformNodeID(rotationTransformNode.GetID()) modelNode.Modified() geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (1, 1, 1), (-167.156, -58.6623, -249.228), [[0.0, 0.0, 1.0], [-0.7071, -0.7071, 0.0], [0.7071, -0.7071, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5221241) # ROI source roiNode = slicer.vtkMRMLAnnotationROINode() roiNode.SetName('SourceROI') slicer.mrmlScene.AddNode(roiNode) roiNode.UnRegister(None) xyz = [0] * 3 center = [0] * 3 slicer.vtkMRMLSliceLogic.GetVolumeRASBox(tinyVolumeNode, xyz, center) radius = [x / 2.0 for x in xyz] roiNode.SetXYZ(center) roiNode.SetRadiusXYZ(radius) geometryWidget.setSourceNode(roiNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue( self.compareOutputGeometry( geometryImageData, (1, 1, 1), (-216.156, -217.711, -135.75), [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846) slicer.util.delayDisplay('Model and ROI source cases - OK') slicer.util.delayDisplay('Segmentation geometry widget test passed')
def splitSegments(self, minimumSize = 0, maxNumberOfSegments = 0, split = True): """ minimumSize: if 0 then it means that all islands are kept, regardless of size maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many """ # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) self.scriptedEffect.saveStateForUndo() # Get modifier labelmap selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(selectedSegmentLabelmap) castIn.SetOutputScalarTypeToUnsignedInt() # 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(False) islandMath.SetMinimumSize(minimumSize) islandMath.Update() # Create a separate image for the first (largest) island labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() if split: thresh.ThresholdBetween(1, 1) else: if maxNumberOfSegments != 0: thresh.ThresholdBetween(1, maxNumberOfSegments) else: thresh.ThresholdByUpper(1) thresh.SetInputData(islandMath.GetOutput()) thresh.SetOutValue(backgroundValue) thresh.SetInValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) thresh.Update() # Create oriented image data from output import vtkSegmentationCorePython as vtkSegmentationCore largestIslandImage = vtkSegmentationCore.vtkOrientedImageData() largestIslandImage.ShallowCopy(thresh.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) largestIslandImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) if split and (maxNumberOfSegments != 1): thresh2 = vtk.vtkImageThreshold() # 0 is background, 1 is largest island; we need label 2 and higher if maxNumberOfSegments != 0: thresh2.ThresholdBetween(2, maxNumberOfSegments) else: thresh2.ThresholdByUpper(2) thresh2.SetInputData(islandMath.GetOutput()) thresh2.SetOutValue(backgroundValue) thresh2.ReplaceInOff() thresh2.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 vtkSegmentationCorePython as vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(thresh2.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID() selectedSegmentIndex = segmentationNode.GetSegmentation().GetSegmentIndex(selectedSegmentID) insertBeforeSegmentID = segmentationNode.GetSegmentation().GetNthSegmentID(selectedSegmentIndex + 1) selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName() slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \ multiLabelImage, segmentationNode, selectedSegmentName+" -", insertBeforeSegmentID ) self.scriptedEffect.modifySelectedSegmentByLabelmap(largestIslandImage, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) qt.QApplication.restoreOverrideCursor()
def TestSection_1_AddRemoveSegment(self): # Add/remove segment from segmentation (check display properties, color table, etc.) logging.info('Test section 1: 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( 'Body_Contour').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( 'Tumor_Contour').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(50) 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() self.assertEqual(imageStat.GetVoxelCount(), 1000) imageStatResult = imageStat.GetOutput() for i in range(4): logging.info("Volume {0}: {1}".format( i, imageStatResult.GetScalarComponentAsDouble(i, 0, 0, 0))) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 795) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 194) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 4) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 7) # 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() self.assertEqual(imageStat.GetVoxelCount(), 1000) imageStatResult = imageStat.GetOutput() for i in range(4): logging.info("Volume {0}: {1}".format( i, imageStatResult.GetScalarComponentAsDouble(i, 0, 0, 0))) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 795) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 194) self.assertEqual( imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 6) 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_2_MergeLabelmapWithDifferentGeometries(self): # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions) logging.info( 'Test section 2: 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 addGrayscaleVolumeStatistics(self): import vtkSegmentationCorePython as vtkSegmentationCore containsLabelmapRepresentation = self.segmentationNode.GetSegmentation( ).ContainsRepresentation( vtkSegmentationCore.vtkSegmentationConverter. GetSegmentationBinaryLabelmapRepresentationName()) if not containsLabelmapRepresentation: return if self.grayscaleNode is None or self.grayscaleNode.GetImageData( ) is None: return # Get geometry of grayscale volume node as oriented image data referenceGeometry_Reference = vtkSegmentationCore.vtkOrientedImageData( ) # reference geometry in reference node coordinate system referenceGeometry_Reference.SetExtent( self.grayscaleNode.GetImageData().GetExtent()) ijkToRasMatrix = vtk.vtkMatrix4x4() self.grayscaleNode.GetIJKToRASMatrix(ijkToRasMatrix) referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix( ijkToRasMatrix) # Get transform between grayscale volume and segmentation segmentationToReferenceGeometryTransform = vtk.vtkGeneralTransform() slicer.vtkMRMLTransformNode.GetTransformBetweenNodes( self.segmentationNode.GetParentTransformNode(), self.grayscaleNode.GetParentTransformNode(), segmentationToReferenceGeometryTransform) cubicMMPerVoxel = reduce(lambda x, y: x * y, referenceGeometry_Reference.GetSpacing()) ccPerCubicMM = 0.001 for segmentID in self.statistics["SegmentIDs"]: segment = self.segmentationNode.GetSegmentation().GetSegment( segmentID) segmentLabelmap = segment.GetRepresentation( vtkSegmentationCore.vtkSegmentationConverter. GetSegmentationBinaryLabelmapRepresentationName()) segmentLabelmap_Reference = vtkSegmentationCore.vtkOrientedImageData( ) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentLabelmap, referenceGeometry_Reference, segmentLabelmap_Reference, False, # nearest neighbor interpolation False, # no padding segmentationToReferenceGeometryTransform) # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(segmentLabelmap_Reference) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) thresh.Update() # Use binary labelmap as a stencil stencil = vtk.vtkImageToImageStencil() stencil.SetInputData(thresh.GetOutput()) stencil.ThresholdByUpper(labelValue) stencil.Update() stat = vtk.vtkImageAccumulate() stat.SetInputData(self.grayscaleNode.GetImageData()) stat.SetStencilData(stencil.GetOutput()) stat.Update() # Add data to statistics list self.statistics[segmentID, "GS voxel count"] = stat.GetVoxelCount() self.statistics[ segmentID, "GS volume mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel self.statistics[segmentID, "GS volume cc"] = stat.GetVoxelCount( ) * cubicMMPerVoxel * ccPerCubicMM if stat.GetVoxelCount() > 0: self.statistics[segmentID, "GS min"] = stat.GetMin()[0] self.statistics[segmentID, "GS max"] = stat.GetMax()[0] self.statistics[segmentID, "GS mean"] = stat.GetMean()[0] self.statistics[segmentID, "GS stdev"] = stat.GetStandardDeviation()[0]
def cutSurfaceWithModel(self, segmentMarkupNode, segmentModel): import vtkSegmentationCorePython as 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().GetNumberOfPolys( ) > 0: operationName = self.scriptedEffect.parameter("Operation") 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()) if operationName in ("FILL_INSIDE", "ERASE_INSIDE", "SET"): stencilToImage.SetInsideValue(1.0) stencilToImage.SetOutsideValue(0.0) else: stencilToImage.SetInsideValue(0.0) stencilToImage.SetOutsideValue(1.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) modMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd if operationName == "ERASE_INSIDE" or operationName == "ERASE_OUTSIDE": modMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove elif operationName == "SET": modMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, modMode) # get fiducial positions as space-separated list import numpy n = segmentMarkupNode.GetNumberOfFiducials() fPos = [] for i in range(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("SurfaceCutEffectMarkupPositions", fPosString)
def getStencilForVolume(self, segmentationNode, segmentID, grayscaleNode): import vtkSegmentationCorePython as vtkSegmentationCore containsLabelmapRepresentation = segmentationNode.GetSegmentation( ).ContainsRepresentation( vtkSegmentationCore.vtkSegmentationConverter. GetSegmentationBinaryLabelmapRepresentationName()) if not containsLabelmapRepresentation: return None if (not grayscaleNode or not grayscaleNode.GetImageData() or not grayscaleNode.GetImageData().GetPointData() or not grayscaleNode.GetImageData().GetPointData().GetScalars()): # Input grayscale node does not contain valid image data return None # Get geometry of grayscale volume node as oriented image data # reference geometry in reference node coordinate system referenceGeometry_Reference = vtkSegmentationCore.vtkOrientedImageData( ) referenceGeometry_Reference.SetExtent( grayscaleNode.GetImageData().GetExtent()) ijkToRasMatrix = vtk.vtkMatrix4x4() grayscaleNode.GetIJKToRASMatrix(ijkToRasMatrix) referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix( ijkToRasMatrix) # Get transform between grayscale volume and segmentation segmentationToReferenceGeometryTransform = vtk.vtkGeneralTransform() slicer.vtkMRMLTransformNode.GetTransformBetweenNodes( segmentationNode.GetParentTransformNode(), grayscaleNode.GetParentTransformNode(), segmentationToReferenceGeometryTransform) segmentLabelmap = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GetBinaryLabelmapRepresentation( segmentID, segmentLabelmap) if (not segmentLabelmap or not segmentLabelmap.GetPointData() or not segmentLabelmap.GetPointData().GetScalars()): # No input label data return None segmentLabelmap_Reference = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentLabelmap, referenceGeometry_Reference, segmentLabelmap_Reference, False, # nearest neighbor interpolation False, # no padding segmentationToReferenceGeometryTransform) # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(segmentLabelmap_Reference) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) thresh.Update() # Use binary labelmap as a stencil stencil = vtk.vtkImageToImageStencil() stencil.SetInputData(thresh.GetOutput()) stencil.ThresholdByUpper(labelValue) stencil.Update() return stencil
def TestSection_2_qMRMLSegmentationGeometryWidget(self): logging.info('Test section 2: qMRMLSegmentationGeometryWidget') import vtkSegmentationCore binaryLabelmapReprName = vtkSegmentationCore.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName() closedSurfaceReprName = vtkSegmentationCore.vtkSegmentationConverter.GetClosedSurfaceRepresentationName() # Use MRHead and Tinypatient for testing import SampleData sampleDataLogic = SampleData.SampleDataLogic() mrVolumeNode = sampleDataLogic.downloadMRHead() [tinyVolumeNode, tinySegmentationNode] = sampleDataLogic.downloadSample('TinyPatient') # Convert MRHead to oriented image data import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic mrOrientedImageData = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.CreateOrientedImageDataFromVolumeNode(mrVolumeNode) mrOrientedImageData.UnRegister(None) # Create segmentation node with binary labelmap master and one segment with MRHead geometry segmentationNode = slicer.vtkMRMLSegmentationNode() segmentationNode.GetSegmentation().SetMasterRepresentationName(binaryLabelmapReprName) geometryStr = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(mrOrientedImageData) segmentationNode.GetSegmentation().SetConversionParameter( vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryStr) slicer.mrmlScene.AddNode(segmentationNode) threshold = vtk.vtkImageThreshold() threshold.SetInputData(mrOrientedImageData) threshold.ThresholdByUpper(16.0) threshold.SetInValue(1) threshold.SetOutValue(0) threshold.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR) threshold.Update() segmentOrientedImageData = vtkSegmentationCore.vtkOrientedImageData() segmentOrientedImageData.DeepCopy(threshold.GetOutput()) mrImageToWorldMatrix = vtk.vtkMatrix4x4() mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix) segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix) segment = vtkSegmentationCore.vtkSegment() segment.SetName('Brain') segment.SetColor(0.0,0.0,1.0) segment.AddRepresentation(binaryLabelmapReprName, segmentOrientedImageData) segmentationNode.GetSegmentation().AddSegment(segment) # Create geometry widget geometryWidget = slicer.qMRMLSegmentationGeometryWidget() geometryWidget.setSegmentationNode(segmentationNode) geometryWidget.editEnabled = True geometryImageData = vtkSegmentationCore.vtkOrientedImageData() # To contain the output later # Volume source with no transforms geometryWidget.setSourceNode(tinyVolumeNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (49,49,23), (248.8439, 248.2890, -123.75), [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]])) slicer.util.delayDisplay('Volume source with no transforms - OK') # Transformed volume source translationTransformMatrix = vtk.vtkMatrix4x4() translationTransformMatrix.SetElement(0,3,24.5) translationTransformMatrix.SetElement(1,3,24.5) translationTransformMatrix.SetElement(2,3,11.5) translationTransformNode = slicer.vtkMRMLLinearTransformNode() translationTransformNode.SetName('TestTranslation') slicer.mrmlScene.AddNode(translationTransformNode) translationTransformNode.SetMatrixTransformToParent(translationTransformMatrix) tinyVolumeNode.SetAndObserveTransformNodeID(translationTransformNode.GetID()) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (49,49,23), (224.3439, 223.7890, -135.25), [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]])) slicer.util.delayDisplay('Transformed volume source - OK') # Volume source with isotropic spacing tinyVolumeNode.SetAndObserveTransformNodeID(None) geometryWidget.setIsotropicSpacing(True) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (23,23,23), (248.8439, 248.2890, -123.75), [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]])) slicer.util.delayDisplay('Volume source with isotropic spacing - OK') # Volume source with oversampling geometryWidget.setIsotropicSpacing(False) geometryWidget.setOversamplingFactor(2.0) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (24.5, 24.5, 11.5), (261.0939, 260.5390, -129.5), [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]])) slicer.util.delayDisplay('Volume source with oversampling - OK') # Segmentation source with binary labelmap master geometryWidget.setOversamplingFactor(1.0) geometryWidget.setSourceNode(tinySegmentationNode) geometryWidget.geometryImageData(geometryImageData) self.assertTrue(self.compareOutputGeometry(geometryImageData, (49,49,23), (248.8439, 248.2890, -123.75), [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]])) slicer.util.delayDisplay('Segmentation source with binary labelmap master - OK') # Segmentation source with closed surface master tinySegmentationNode.GetSegmentation().SetConversionParameter('Smoothing factor', '0.0') self.assertTrue(tinySegmentationNode.GetSegmentation().CreateRepresentation(closedSurfaceReprName)) tinySegmentationNode.GetSegmentation().SetMasterRepresentationName(closedSurfaceReprName) tinySegmentationNode.Modified() # Trigger re-calculation of geometry (only generic Modified event is observed) geometryWidget.geometryImageData(geometryImageData) # Manually resample and check if the output is non-empty # Note: This is done for all poly data based geometry calculations below #TODO: Come up with proper geometry baselines for these too vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertTrue(self.imageDataContainsData(geometryImageData)) slicer.util.delayDisplay('Segmentation source with closed surface master - OK') # Model source with no transform outputModelHierarchy = slicer.vtkMRMLModelHierarchyNode() slicer.mrmlScene.AddNode(outputModelHierarchy) success = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ExportVisibleSegmentsToModelHierarchy( tinySegmentationNode, outputModelHierarchy ) self.assertTrue(success) modelNode = slicer.util.getNode('Body_Contour') geometryWidget.setSourceNode(modelNode) geometryWidget.geometryImageData(geometryImageData) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertTrue(self.imageDataContainsData(geometryImageData)) slicer.util.delayDisplay('Model source with no transform - OK') # Transformed model source rotationTransform = vtk.vtkTransform() rotationTransform.RotateX(45) rotationTransformMatrix = vtk.vtkMatrix4x4() rotationTransform.GetMatrix(rotationTransformMatrix) rotationTransformNode = slicer.vtkMRMLLinearTransformNode() rotationTransformNode.SetName('TestRotation') slicer.mrmlScene.AddNode(rotationTransformNode) rotationTransformNode.SetMatrixTransformToParent(rotationTransformMatrix) modelNode.SetAndObserveTransformNodeID(rotationTransformNode.GetID()) modelNode.Modified() geometryWidget.geometryImageData(geometryImageData) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertTrue(self.imageDataContainsData(geometryImageData)) slicer.util.delayDisplay('Transformed model source - OK') # ROI source roiNode = slicer.vtkMRMLAnnotationROINode() roiNode.SetName('SourceROI') slicer.mrmlScene.AddNode(roiNode) roiNode.UnRegister(None) xyz = [0]*3 center = [0]*3 slicer.vtkMRMLSliceLogic.GetVolumeRASBox(tinyVolumeNode, xyz, center) radius = map(lambda x: x/2.0, xyz) roiNode.SetXYZ(center) roiNode.SetRadiusXYZ(radius) geometryWidget.setSourceNode(roiNode) geometryWidget.geometryImageData(geometryImageData) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( segmentOrientedImageData, geometryImageData, geometryImageData, False, True) self.assertTrue(self.imageDataContainsData(geometryImageData)) slicer.util.delayDisplay('ROI source - OK') slicer.util.delayDisplay('Segmentation geometry widget test passed')
def fastMarching(self, percentMax): self.fm = None # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() # Get segmentation 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.ShallowCopy( imageCast.GetOutput()) # Copy image data masterImageDataShort.CopyDirections( masterImageData) # Copy geometry masterImageData = masterImageDataShort if not self.originalSelectedSegmentLabelmap: segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() segmentationNode.GetSegmentation().SeparateSegmentLabelmap( self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()) selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() if not self.originalSelectedSegmentLabelmap: self.originalSelectedSegmentLabelmap = vtkSegmentationCore.vtkOrientedImageData( ) self.originalSelectedSegmentLabelmap.DeepCopy( selectedSegmentLabelmap) self.selectedSegmentId = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_SHORT) thresh.Update() labelImage = thresh.GetOutput() # collect seeds dim = masterImageData.GetDimensions() # initialize the filter import vtkSlicerSegmentEditorFastMarchingModuleLogicPython self.fm = vtkSlicerSegmentEditorFastMarchingModuleLogicPython.vtkPichonFastMarching( ) scalarRange = masterImageData.GetScalarRange() depth = scalarRange[1] - scalarRange[0] # this is more or less arbitrary; large depth values will bring the # algorithm to the knees scaleValue = 0 shiftValue = 0 if depth > 300: scaleValue = 300. / depth if scalarRange[0] < 0: shiftValue = scalarRange[0] * -1 if scaleValue or shiftValue: rescale = vtk.vtkImageShiftScale() rescale.SetInputData(masterImageData) rescale.SetScale(scaleValue) rescale.SetShift(shiftValue) rescale.Update() masterImageData = rescale.GetOutput() scalarRange = masterImageData.GetScalarRange() depth = scalarRange[1] - scalarRange[0] self.fm.init(dim[0], dim[1], dim[2], depth, 1, 1, 1) caster = vtk.vtkImageCast() caster.SetOutputScalarTypeToShort() caster.SetInputData(masterImageData) self.fm.SetInputConnection(caster.GetOutputPort()) # self.fm.SetOutput(labelImage) npoints = int(dim[0] * dim[1] * dim[2] * percentMax / 100.) self.fm.setNPointsEvolution(npoints) self.fm.setActiveLabel(labelValue) spacing = self.originalSelectedSegmentLabelmap.GetSpacing() self.voxelVolume = spacing[0] * spacing[1] * spacing[2] self.totalNumberOfVoxels = npoints nSeeds = self.fm.addSeedsFromImage(labelImage) if nSeeds == 0: self.totalNumberOfVoxels = 0 return self.fm.Modified() self.fm.Update() # Need to call show() twice for data to be updated. # There are many other issues with the vtkPichonFastMarching filter # (expects extents to start at 0, crashes in debug mode, etc). self.fm.show(1) self.fm.Modified() self.fm.Update() self.updateLabel(self.marcher.value / self.marcher.maximum) logging.info('FastMarching march update completed')
def splitSegments(self, minimumSize=0, maxNumberOfSegments=0, split=True): """ minimumSize: if 0 then it means that all islands are kept, regardless of size maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many """ # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) self.scriptedEffect.saveStateForUndo() # Get modifier labelmap selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() castIn = vtk.vtkImageCast() castIn.SetInputData(selectedSegmentLabelmap) castIn.SetOutputScalarTypeToUnsignedInt() # 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(False) islandMath.SetMinimumSize(minimumSize) islandMath.Update() # Create a separate image for the first (largest) island labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() if split: thresh.ThresholdBetween(1, 1) else: if maxNumberOfSegments != 0: thresh.ThresholdBetween(1, maxNumberOfSegments) else: thresh.ThresholdByUpper(1) thresh.SetInputData(islandMath.GetOutput()) thresh.SetOutValue(backgroundValue) thresh.SetInValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) thresh.Update() # Create oriented image data from output import vtkSegmentationCorePython as vtkSegmentationCore largestIslandImage = vtkSegmentationCore.vtkOrientedImageData() largestIslandImage.ShallowCopy(thresh.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) largestIslandImage.SetImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) if split and (maxNumberOfSegments != 1): thresh2 = vtk.vtkImageThreshold() # 0 is background, 1 is largest island; we need label 2 and higher if maxNumberOfSegments != 0: thresh2.ThresholdBetween(2, maxNumberOfSegments) else: thresh2.ThresholdByUpper(2) thresh2.SetInputData(islandMath.GetOutput()) thresh2.SetOutValue(backgroundValue) thresh2.ReplaceInOff() thresh2.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 vtkSegmentationCorePython as vtkSegmentationCore multiLabelImage = vtkSegmentationCore.vtkOrientedImageData() multiLabelImage.DeepCopy(thresh2.GetOutput()) selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4() selectedSegmentLabelmap.GetImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) multiLabelImage.SetGeometryFromImageToWorldMatrix( selectedSegmentLabelmapImageToWorldMatrix) # Import multi-label labelmap to segmentation segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() selectedSegmentID = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() selectedSegmentIndex = segmentationNode.GetSegmentation( ).GetSegmentIndex(selectedSegmentID) insertBeforeSegmentID = segmentationNode.GetSegmentation( ).GetNthSegmentID(selectedSegmentIndex + 1) selectedSegmentName = segmentationNode.GetSegmentation( ).GetSegment(selectedSegmentID).GetName() slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \ multiLabelImage, segmentationNode, selectedSegmentName+" -", insertBeforeSegmentID ) self.scriptedEffect.modifySelectedSegmentByLabelmap( largestIslandImage, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) qt.QApplication.restoreOverrideCursor()
def onApply(self): self.scriptedEffect.saveStateForUndo() import vtkSegmentationCorePython as vtkSegmentationCore # Get modifier labelmap and parameters operation = self.scriptedEffect.parameter("Operation") bypassMasking = (self.scriptedEffect.integerParameter("BypassMasking") != 0) selectedSegmentID = self.scriptedEffect.parameterSetNode( ).GetSelectedSegmentID() segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() segmentation = segmentationNode.GetSegmentation() if operation in self.operationsRequireModifierSegment: # Get modifier segment modifierSegmentID = self.modifierSegmentID() if not modifierSegmentID: logging.error( "Operation {0} requires a selected modifier segment". format(operation)) return modifierSegment = segmentation.GetSegment(modifierSegmentID) modifierSegmentLabelmap = modifierSegment.GetRepresentation( vtkSegmentationCore.vtkSegmentationConverter. GetSegmentationBinaryLabelmapRepresentationName()) # Get common geometry commonGeometryString = segmentationNode.GetSegmentation( ).DetermineCommonLabelmapGeometry( vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS, None) if not commonGeometryString: logging.info( "Logical operation skipped: all segments are empty") return commonGeometryImage = vtkSegmentationCore.vtkOrientedImageData() vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry( commonGeometryString, commonGeometryImage, False) # Make sure modifier segment has correct geometry # (if modifier segment has been just copied over from another segment then its geometry may be different) if not vtkSegmentationCore.vtkOrientedImageDataResample.DoGeometriesMatch( commonGeometryImage, modifierSegmentLabelmap): modifierSegmentLabelmap_CommonGeometry = vtkSegmentationCore.vtkOrientedImageData( ) vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage( modifierSegmentLabelmap, commonGeometryImage, modifierSegmentLabelmap_CommonGeometry, False, # nearest neighbor interpolation, True # make sure resampled modifier segment is not cropped ) modifierSegmentLabelmap = modifierSegmentLabelmap_CommonGeometry if operation == LOGICAL_COPY: if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( modifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_UNION: if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( modifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer. vtkSlicerSegmentationsModuleLogic.MODE_MERGE_MAX, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeAdd) elif operation == LOGICAL_SUBTRACT: if bypassMasking: invertedModifierSegmentLabelmap = self.getInvertedBinaryLabelmap( modifierSegmentLabelmap) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( invertedModifierSegmentLabelmap, segmentationNode, selectedSegmentID, slicer. vtkSlicerSegmentationsModuleLogic.MODE_MERGE_MIN, modifierSegmentLabelmap.GetExtent()) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect. ModificationModeRemove) elif operation == LOGICAL_INTERSECT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) intersectionLabelmap = vtkSegmentationCore.vtkOrientedImageData( ) vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage( selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap, vtkSegmentationCore. vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent()) selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent( ) modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent( ) commonExtent = [ max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]), min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]), max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]), min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]), max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]), min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5]) ] if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( intersectionLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE, commonExtent) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( intersectionLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent) elif operation == LOGICAL_INVERT: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) invertedSelectedSegmentLabelmap = self.getInvertedBinaryLabelmap( selectedSegmentLabelmap) if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( invertedSelectedSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( invertedSelectedSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) elif operation == LOGICAL_CLEAR or operation == LOGICAL_FILL: selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap( ) vtkSegmentationCore.vtkOrientedImageDataResample.FillImage( selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent()) if bypassMasking: slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( selectedSegmentLabelmap, segmentationNode, selectedSegmentID, slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE) else: self.scriptedEffect.modifySelectedSegmentByLabelmap( selectedSegmentLabelmap, slicer. qSlicerSegmentEditorAbstractEffect.ModificationModeSet) else: logging.error("Unknown operation: {0}".format(operation))
def preview(self): # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() # Get segmentation segmentationNode = self.scriptedEffect.parameterSetNode( ).GetSegmentationNode() previewNode = self.getPreviewNode() if not previewNode or not self.mergedLabelmapGeometryImage \ or (self.clippedMasterImageDataRequired and not self.clippedMasterImageData): self.reset() # Compute merged labelmap extent (effective extent slightly expanded) 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)) return if not self.mergedLabelmapGeometryImage: self.mergedLabelmapGeometryImage = vtkSegmentationCore.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) masterImageExtent = masterImageData.GetExtent() labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent( ) margin = [17, 17, 17] 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]) ] self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent) previewNode = slicer.mrmlScene.CreateNodeByClass( 'vtkMRMLSegmentationNode') previewNode.UnRegister(None) previewNode = slicer.mrmlScene.AddNode(previewNode) 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) if self.clippedMasterImageDataRequired: self.clippedMasterImageData = vtkSegmentationCore.vtkOrientedImageData( ) masterImageClipper = vtk.vtkImageConstantPad() masterImageClipper.SetInputData(masterImageData) masterImageClipper.SetOutputWholeExtent( self.mergedLabelmapGeometryImage.GetExtent()) masterImageClipper.Update() self.clippedMasterImageData.ShallowCopy( masterImageClipper.GetOutput()) self.clippedMasterImageData.CopyDirections( self.mergedLabelmapGeometryImage) previewNode.SetName(segmentationNode.GetName() + " preview") mergedImage = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments( mergedImage, vtkSegmentationCore.vtkSegmentation. EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds) outputLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.computePreviewLabelmap(mergedImage, outputLabelmap) # Write output segmentation results in segments for index in xrange(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 = vtkSegmentationCore.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) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment( newSegmentLabelmap, previewNode, segmentID) self.updateGUIFromMRML()
def preview(self): # Get master volume image data import vtkSegmentationCorePython as vtkSegmentationCore masterImageData = self.scriptedEffect.masterVolumeImageData() # Get segmentation segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode() previewNode = self.getPreviewNode() if (not previewNode or not self.mergedLabelmapGeometryImage or (self.clippedMasterImageDataRequired and not self.clippedMasterImageData)): self.reset() # Compute merged labelmap extent (effective extent slightly expanded) 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)) return if not self.mergedLabelmapGeometryImage: self.mergedLabelmapGeometryImage = vtkSegmentationCore.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) 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 = vtkSegmentationCore.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 = vtkSegmentationCore.vtkOrientedImageData() intensityBasedMasking = self.scriptedEffect.parameterSetNode().GetMasterVolumeIntensityMask() 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 previewNode.SetName(segmentationNode.GetName()+" preview") mergedImage = vtkSegmentationCore.vtkOrientedImageData() segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds) outputLabelmap = vtkSegmentationCore.vtkOrientedImageData() self.computePreviewLabelmap(mergedImage, outputLabelmap) # Write output segmentation results in segments for index in xrange(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 = vtkSegmentationCore.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) slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, previewNode, segmentID) # Automatically hide result segments that are background (all eight corners are non-zero) previewNode.GetDisplayNode().SetSegmentVisibility3D(segmentID, not self.isBackgroundLabelmap(newSegmentLabelmap)) self.updateGUIFromMRML()