def mergeStructures(self,label="all"): """merge the named or all structure labels into the master label""" merge = self.merge if not merge: return rows = self.structures.rowCount() # check that structures are all the same size as the merge volume dims = merge.GetImageData().GetDimensions() for row in xrange(rows): structureName = self.structures.item(row,2).text() structureVolume = self.structureVolume( structureName ) if not structureVolume: mergeName = merge.GetName() slicer.util.errorDisplay( "Merge Aborted: No image data for volume node %s."%(structureName) ) return if structureVolume.GetImageData().GetDimensions() != dims: mergeName = merge.GetName() slicer.util.errorDisplay( "Merge Aborted: Volume %s does not have the same dimensions as the target merge volume. Use the Resample Scalar/Vector/DWI module to resample. Use %s as the Reference Volume and select Nearest Neighbor (nn) Interpolation Type."%(structureName,mergeName) ) return # check that user really wants to merge rows = self.structures.rowCount() for row in xrange(rows): structureName = self.structures.item(row,2).text() structureVolume = self.structureVolume( structureName) if structureVolume.GetImageData().GetMTime() < merge.GetImageData().GetMTime(): mergeName = merge.GetName() slicer.util.errorDisplay( "Note: Merge volume has been modified more recently than structure volumes.\nCreating backup copy as %s-backup"%mergeName ) self.volumesLogic.CloneVolume( slicer.mrmlScene, merge, mergeName+"-backup" ) # # find the Image Label Combine # - call Enter to be sure GUI has been built # combiner = slicer.vtkImageLabelCombine() # # iterate through structures merging into merge volume # for row in xrange(rows): structureName = self.structures.item(row,2).text() structureVolume = self.structureVolume( structureName ) if row == 0: # first row, just copy into merge volume merge.GetImageData().DeepCopy( structureVolume.GetImageData() ) continue combiner.SetInputConnection(0, merge.GetImageDataConnection() ) combiner.SetInputConnection(1, structureVolume.GetImageDataConnection() ) self.statusText( "Merging %s" % structureName ) combiner.Update() merge.GetImageData().DeepCopy( combiner.GetOutput() ) # mark all volumes as modified so we will be able to tell if the # merged volume gets edited after these for row in xrange(rows): structureName = self.structures.item(row,2).text() structureVolume = self.structureVolume( structureName ) structureVolume.GetImageData().Modified() EditUtil.setActiveVolumes(self.master, merge) self.statusText( "Finished merging." )
def load(self,loadable): """ Load the DICOM SEG object """ print('DICOM SEG load()') labelNodes = vtk.vtkCollection() uid = None try: uid = loadable.uid print ('in load(): uid = ', uid) except AttributeError: return False res = False # make the output directory outputDir = os.path.join(slicer.app.temporaryPath,"QIICR","SEG",loadable.uid) try: os.makedirs(outputDir) except: pass # produces output label map files, one per segment, and information files with # the terminology information for each segment segFileName = slicer.dicomDatabase.fileForInstance(uid) if segFileName is None: print 'Failed to get the filename from the DICOM database for ', uid return False parameters = { "inputSEGFileName": segFileName, "outputDirName": outputDir, } seg2nrrd = None try: seg2nrrd = slicer.modules.seg2nrrd except AttributeError: print 'Unable to find CLI module SEG2NRRD, unable to load DICOM Segmentation object' return False cliNode = None cliNode = slicer.cli.run(seg2nrrd, cliNode, parameters, wait_for_completion=True) if cliNode.GetStatusString() != 'Completed': print 'SEG2NRRD did not complete successfully, unable to load DICOM Segmentation' return False # create a new color node to be set up with the colors in these segments colorLogic = slicer.modules.colors.logic() segmentationColorNode = slicer.vtkMRMLColorTableNode() segmentationColorNode.SetName(loadable.name) segmentationColorNode.SetTypeToUser(); segmentationColorNode.SetHideFromEditors(0) segmentationColorNode.SetAttribute("Category", "File") segmentationColorNode.NamesInitialisedOff() slicer.mrmlScene.AddNode(segmentationColorNode) # also create a new terminology and associate it with this color node colorLogic.CreateNewTerminology(segmentationColorNode.GetName()) import glob numberOfSegments = len(glob.glob(os.path.join(outputDir,'*.nrrd'))) # resize the color table to include the segments plus 0 for the background print ('number of segments = ',numberOfSegments) segmentationColorNode.SetNumberOfColors(numberOfSegments + 1) segmentationColorNode.SetColor(0, 'background', 0.0, 0.0, 0.0, 0.0) seriesName = self.referencedSeriesName(loadable) segmentNodes = [] for segmentId in range(numberOfSegments): # load each of the segments' segmentations # Initialize color and terminology from .info file # See SEG2NRRD.cxx and EncodeSEG.cxx for how it's written. # Format of the .info file (no leading spaces, labelNum, RGBColor, SegmentedPropertyCategory and # SegmentedPropertyCategory are required): # labelNum;RGB:R,G,B;SegmentedPropertyCategory:code,scheme,meaning;SegmentedPropertyType:code,scheme,meaning;SegmentedPropertyTypeModifier:code,scheme,meaning;AnatomicRegion:code,scheme,meaning;AnatomicRegionModifier:code,scheme,meaning # R, G, B are 0-255 in file, but mapped to 0-1 for use in color node # set defaults in case of missing fields, modifiers are optional rgb = (0., 0., 0.) colorIndex = segmentId + 1 categoryCode = 'T-D0050' categoryCodingScheme = 'SRT' categoryCodeMeaning = 'Tissue' typeCode = 'T-D0050' typeCodeMeaning = 'Tissue' typeCodingScheme = 'SRT' typeModCode = '' typeModCodingScheme = '' typeModCodeMeaning = '' regionCode = 'T-D0010' regionCodingScheme = 'SRT' regionCodeMeaning = 'Entire Body' regionModCode = '' regionModCodingScheme = '' regionModCodeMeaning = '' infoFileName = os.path.join(outputDir,str(segmentId+1)+".info") print ('Parsing info file', infoFileName) with open(infoFileName, 'r') as infoFile: for line in infoFile: line = line.rstrip() if len(line) == 0: # empty line continue terms = line.split(';') for term in terms: # label number is the first thing, no key if len(term.split(':')) == 1: colorIndex = int(term) else: key = term.split(':')[0] if key == "RGB": rgb255 = term.split(':')[1].split(',') rgb = map(lambda c: float(c) / 255., rgb255) elif key == "AnatomicRegion": # Get the Region information region = term.split(':')[1] # use partition to deal with any commas in the meaning regionCode, sep, regionCodingSchemeAndCodeMeaning = region.partition(',') regionCodingScheme, sep, regionCodeMeaning = regionCodingSchemeAndCodeMeaning.partition(',') elif key == "AnatomicRegionModifier": regionMod = term.split(':')[1] regionModCode, sep, regionModCodingSchemeAndCodeMeaning = regionMod.partition(',') regionModCodingScheme, sep, regionModCodeMeaning = regionModCodingSchemeAndCodeMeaning.partition(',') elif key == "SegmentedPropertyCategory": # Get the Category information category = term.split(':')[1] categoryCode, sep, categoryCodingSchemeAndCodeMeaning = category.partition(',') categoryCodingScheme, sep, categoryCodeMeaning = categoryCodingSchemeAndCodeMeaning.partition(',') elif key == "SegmentedPropertyType": # Get the Type information types = term.split(':')[1] typeCode, sep, typeCodingSchemeAndCodeMeaning = types.partition(',') typeCodingScheme, sep, typeCodeMeaning = typeCodingSchemeAndCodeMeaning.partition(',') elif key == "SegmentedPropertyTypeModifier": typeMod = term.split(':')[1] typeModCode, sep, typeModCodingSchemeAndCodeMeaning = typeMod.partition(',') typeModCodingScheme, sep, typeModCodeMeaning = typeModCodingSchemeAndCodeMeaning.partition(',') # set the color name from the terminology colorName = typeCodeMeaning segmentationColorNode.SetColor(colorIndex, colorName, *rgb) colorLogic.AddTermToTerminology(segmentationColorNode.GetName(), colorIndex, categoryCode, categoryCodingScheme, categoryCodeMeaning, typeCode, typeCodingScheme, typeCodeMeaning, typeModCode, typeModCodingScheme, typeModCodeMeaning, regionCode, regionCodingScheme, regionCodeMeaning, regionModCode, regionModCodingScheme, regionModCodeMeaning) # end of processing a line of terminology infoFile.close() #TODO: Create logic class that both CLI and this plugin uses so that we don't need to have temporary NRRD files and labelmap nodes #if not hasattr(slicer.modules, 'segmentations'): # load the segmentation volume file and name it for the reference series and segment color labelFileName = os.path.join(outputDir,str(segmentId+1)+".nrrd") segmentName = seriesName + "-" + colorName + "-label" (success,labelNode) = slicer.util.loadLabelVolume(labelFileName, properties = {'name' : segmentName}, returnNode=True) segmentNodes.append(labelNode) # point the label node to the color node we're creating labelDisplayNode = labelNode.GetDisplayNode() if labelDisplayNode == None: print ('Warning: no label map display node for segment ',segmentId,', creating!') labelNode.CreateDefaultDisplayNodes() labelDisplayNode = labelNode.GetDisplayNode() labelDisplayNode.SetAndObserveColorNodeID(segmentationColorNode.GetID()) # TODO: initialize referenced UID (and segment number?) attribute(s) # create Subject hierarchy nodes for the loaded series self.addSeriesInSubjectHierarchy(loadable, labelNode) # create a combined (merge) label volume node (only if a segment was created) if labelNode: volumeLogic = slicer.modules.volumes.logic() mergeNode = volumeLogic.CloneVolume(labelNode, seriesName + "-label") combiner = slicer.vtkImageLabelCombine() for segmentNode in segmentNodes: combiner.SetInputConnection(0, mergeNode.GetImageDataConnection() ) combiner.SetInputConnection(1, segmentNode.GetImageDataConnection() ) combiner.Update() mergeNode.GetImageData().DeepCopy( combiner.GetOutput() ) for segmentNode in segmentNodes: segmentNode.Modified() # sets the MTime so the editor won't think they are older than mergeNode # display the mergeNode selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetReferenceActiveLabelVolumeID( mergeNode.GetID() ) slicer.app.applicationLogic().PropagateVolumeSelection(0) # finalize the color node segmentationColorNode.NamesInitialisedOn() # TODO: the outputDir should be cleaned up if hasattr(slicer.modules, 'segmentations'): import vtkSlicerSegmentationsModuleLogic import vtkSlicerSegmentationsModuleMRML import vtkSegmentationCore segmentationNode = vtkSlicerSegmentationsModuleMRML.vtkMRMLSegmentationNode() segmentationNode.SetName(seriesName) segmentationNode.AddNodeReferenceID('colorNodeID', segmentationColorNode.GetID()) slicer.mrmlScene.AddNode(segmentationNode) segmentationDisplayNode = vtkSlicerSegmentationsModuleMRML.vtkMRMLSegmentationDisplayNode() segmentationNode.SetAndObserveDisplayNodeID(segmentationDisplayNode.GetID()) slicer.mrmlScene.AddNode(segmentationDisplayNode) segmentation = vtkSegmentationCore.vtkSegmentation() segmentation.SetMasterRepresentationName(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()) segmentationNode.SetAndObserveSegmentation(segmentation) self.addSeriesInSubjectHierarchy(loadable, segmentationNode) colorID = 1 for segmentNode in segmentNodes: segment = vtkSegmentationCore.vtkSegment() segment.SetName(segmentNode.GetName()) segmentColor = [0,0,0,0] segmentationColorNode.GetColor(colorID, segmentColor) segment.SetDefaultColor(segmentColor[0:3]) colorID += 1 #TODO: when the logic class is created, this will need to be changed logic = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic() orientedImage = logic.CreateOrientedImageDataFromVolumeNode(segmentNode) segment.AddRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName(), orientedImage) segmentation.AddSegment(segment) segmentDisplayNode = segmentNode.GetDisplayNode() slicer.mrmlScene.RemoveNode(segmentDisplayNode) slicer.mrmlScene.RemoveNode(segmentNode) segmentation.CreateRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName(), True) slicer.mrmlScene.RemoveNode(mergeNode) return True
def mergeStructures(self, label="all"): """merge the named or all structure labels into the master label""" merge = self.merge if not merge: return rows = self.structures.rowCount() # check that structures are all the same size as the merge volume dims = merge.GetImageData().GetDimensions() for row in xrange(rows): structureName = self.structures.item(row, 2).text() structureVolume = self.structureVolume(structureName) if not structureVolume: mergeName = merge.GetName() slicer.util.errorDisplay( "Merge Aborted: No image data for volume node %s." % (structureName)) return if structureVolume.GetImageData().GetDimensions() != dims: mergeName = merge.GetName() slicer.util.errorDisplay( "Merge Aborted: Volume %s does not have the same dimensions as the target merge volume. Use the Resample Scalar/Vector/DWI module to resample. Use %s as the Reference Volume and select Nearest Neighbor (nn) Interpolation Type." % (structureName, mergeName)) return # check that user really wants to merge rows = self.structures.rowCount() for row in xrange(rows): structureName = self.structures.item(row, 2).text() structureVolume = self.structureVolume(structureName) if structureVolume.GetImageData().GetMTime() < merge.GetImageData( ).GetMTime(): mergeName = merge.GetName() slicer.util.errorDisplay( "Note: Merge volume has been modified more recently than structure volumes.\nCreating backup copy as %s-backup" % mergeName) self.volumesLogic.CloneVolume(slicer.mrmlScene, merge, mergeName + "-backup") # # find the Image Label Combine # - call Enter to be sure GUI has been built # combiner = slicer.vtkImageLabelCombine() # # iterate through structures merging into merge volume # for row in xrange(rows): structureName = self.structures.item(row, 2).text() structureVolume = self.structureVolume(structureName) if row == 0: # first row, just copy into merge volume merge.GetImageData().DeepCopy(structureVolume.GetImageData()) continue if vtk.VTK_MAJOR_VERSION <= 5: combiner.SetInput1(merge.GetImageData()) combiner.SetInput2(structureVolume.GetImageData()) else: combiner.SetInputConnection(0, merge.GetImageDataConnection()) combiner.SetInputConnection( 1, structureVolume.GetImageDataConnection()) self.statusText("Merging %s" % structureName) combiner.Update() merge.GetImageData().DeepCopy(combiner.GetOutput()) # mark all volumes as modified so we will be able to tell if the # merged volume gets edited after these for row in xrange(rows): structureName = self.structures.item(row, 2).text() structureVolume = self.structureVolume(structureName) structureVolume.GetImageData().Modified() EditUtil.setActiveVolumes(self.master, merge) self.statusText("Finished merging.")
def load(self, loadable): """ Call Reporting logic to load the DICOM SEG object """ print('DICOM SEG load()') labelNodes = vtk.vtkCollection() uid = None try: reportingLogic = slicer.modules.reporting.logic() uid = loadable.uid print('in load(): uid = ', uid) except AttributeError: return False res = False # make the output directory outputDir = os.path.join(slicer.app.temporaryPath, "QIICR", "SEG", loadable.uid) try: os.makedirs(outputDir) except: pass # produces output label map files, one per segment, and information files with # the terminology information for each segment res = reportingLogic.DicomSegRead(labelNodes, uid) # create a new color node to be set up with the colors in these segments colorLogic = slicer.modules.colors.logic() segmentationColorNode = slicer.vtkMRMLColorTableNode() segmentationColorNode.SetName(loadable.name) segmentationColorNode.SetTypeToUser() segmentationColorNode.SetHideFromEditors(0) segmentationColorNode.SetAttribute("Category", "File") segmentationColorNode.NamesInitialisedOff() slicer.mrmlScene.AddNode(segmentationColorNode) # also create a new terminology and associate it with this color node colorLogic.CreateNewTerminology(segmentationColorNode.GetName()) import glob numberOfSegments = len(glob.glob(os.path.join(outputDir, '*.nrrd'))) # resize the color table to include the segments plus 0 for the background print('number of segments = ', numberOfSegments) segmentationColorNode.SetNumberOfColors(numberOfSegments + 1) segmentationColorNode.SetColor(0, 'background', 0.0, 0.0, 0.0, 0.0) seriesName = self.referencedSeriesName(loadable) segmentNodes = [] for segmentId in range(numberOfSegments): # load each of the segments' segmentations # Initialize color and terminology from .info file # See SEG2NRRD.cxx and EncodeSEG.cxx for how it's written. # Format of the .info file (no leading spaces, labelNum, RGBColor, SegmentedPropertyCategory and # SegmentedPropertyCategory are required): # labelNum;RGB:R,G,B;SegmentedPropertyCategory:code,scheme,meaning;SegmentedPropertyType:code,scheme,meaning;SegmentedPropertyTypeModifier:code,scheme,meaning;AnatomicRegion:code,scheme,meaning;AnatomicRegionModifier:code,scheme,meaning # R, G, B are 0-255 in file, but mapped to 0-1 for use in color node # set defaults in case of missing fields, modifiers are optional rgb = (0., 0., 0.) colorIndex = segmentId + 1 categoryCode = 'T-D0050' categoryCodingScheme = 'SRT' categoryCodeMeaning = 'Tissue' typeCode = 'T-D0050' typeCodeMeaning = 'Tissue' typeCodingScheme = 'SRT' typeModCode = '' typeModCodingScheme = '' typeModCodeMeaning = '' regionCode = 'T-D0010' regionCodingScheme = 'SRT' regionCodeMeaning = 'Entire Body' regionModCode = '' regionModCodingScheme = '' regionModCodeMeaning = '' infoFileName = os.path.join(outputDir, str(segmentId + 1) + ".info") print('Parsing info file', infoFileName) with open(infoFileName, 'r') as infoFile: for line in infoFile: line = line.rstrip() if len(line) == 0: # empty line continue terms = line.split(';') for term in terms: # label number is the first thing, no key if len(term.split(':')) == 1: colorIndex = int(term) else: key = term.split(':')[0] if key == "RGB": rgb255 = term.split(':')[1].split(',') rgb = map(lambda c: float(c) / 255., rgb255) elif key == "AnatomicRegion": # Get the Region information region = term.split(':')[1] # use partition to deal with any commas in the meaning regionCode, sep, regionCodingSchemeAndCodeMeaning = region.partition( ',') regionCodingScheme, sep, regionCodeMeaning = regionCodingSchemeAndCodeMeaning.partition( ',') elif key == "AnatomicRegionModifier": regionMod = term.split(':')[1] regionModCode, sep, regionModCodingSchemeAndCodeMeaning = regionMod.partition( ',') regionModCodingScheme, sep, regionModCodeMeaning = regionModCodingSchemeAndCodeMeaning.partition( ',') elif key == "SegmentedPropertyCategory": # Get the Category information category = term.split(':')[1] categoryCode, sep, categoryCodingSchemeAndCodeMeaning = category.partition( ',') categoryCodingScheme, sep, categoryCodeMeaning = categoryCodingSchemeAndCodeMeaning.partition( ',') elif key == "SegmentedPropertyType": # Get the Type information types = term.split(':')[1] typeCode, sep, typeCodingSchemeAndCodeMeaning = types.partition( ',') typeCodingScheme, sep, typeCodeMeaning = typeCodingSchemeAndCodeMeaning.partition( ',') elif key == "SegmentedPropertyTypeModifier": typeMod = term.split(':')[1] typeModCode, sep, typeModCodingSchemeAndCodeMeaning = typeMod.partition( ',') typeModCodingScheme, sep, typeModCodeMeaning = typeModCodingSchemeAndCodeMeaning.partition( ',') # set the color name from the terminology colorName = typeCodeMeaning segmentationColorNode.SetColor(colorIndex, colorName, *rgb) colorLogic.AddTermToTerminology( segmentationColorNode.GetName(), colorIndex, categoryCode, categoryCodingScheme, categoryCodeMeaning, typeCode, typeCodingScheme, typeCodeMeaning, typeModCode, typeModCodingScheme, typeModCodeMeaning, regionCode, regionCodingScheme, regionCodeMeaning, regionModCode, regionModCodingScheme, regionModCodeMeaning) # end of processing a line of terminology infoFile.close() # load the segmentation volume file and name it for the reference series and segment color labelFileName = os.path.join(outputDir, str(segmentId + 1) + ".nrrd") segmentName = seriesName + "-" + colorName + "-label" (success, labelNode) = slicer.util.loadLabelVolume( labelFileName, properties={'name': segmentName}, returnNode=True) segmentNodes.append(labelNode) # point the label node to the color node we're creating labelDisplayNode = labelNode.GetDisplayNode() if labelDisplayNode == None: print('Warning: no label map display node for segment ', segmentId, ', creating!') labelNode.CreateDefaultDisplayNodes() labelDisplayNode = labelNode.GetDisplayNode() labelDisplayNode.SetAndObserveColorNodeID( segmentationColorNode.GetID()) # TODO: initialize referenced UID (and segment number?) attribute(s) # create Subject hierarchy nodes for the loaded series self.addSeriesInSubjectHierarchy(loadable, labelNode) # create a combined (merge) label volume node (only if a segment was created) if labelNode: volumeLogic = slicer.modules.volumes.logic() mergeNode = volumeLogic.CloneVolume(labelNode, seriesName + "-label") combiner = slicer.vtkImageLabelCombine() for segmentNode in segmentNodes: combiner.SetInputConnection(0, mergeNode.GetImageDataConnection()) combiner.SetInputConnection( 1, segmentNode.GetImageDataConnection()) combiner.Update() mergeNode.GetImageData().DeepCopy(combiner.GetOutput()) for segmentNode in segmentNodes: segmentNode.Modified( ) # sets the MTime so the editor won't think they are older than mergeNode # display the mergeNode selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetReferenceActiveLabelVolumeID(mergeNode.GetID()) slicer.app.applicationLogic().PropagateVolumeSelection(0) # finalize the color node segmentationColorNode.NamesInitialisedOn() # TODO: the outputDir should be cleaned up return True