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
Пример #3
0
    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.")
Пример #4
0
    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