Python example that convert the multiple-label segmentation to binary-label segmentation

Hello all,
I have a dataset from TCIA, which are multiple-label segmentation file (NOT nrrd but dcm file). the label map is something like this that I can read out by leveraging pydicom:

{'Segment_1': {'Label': 'Liver', 'Description': 'Anatomical Structure'}, 'Segment_2': {'Label': 'Liver Remnant', 'Description': 'Anatomical Structure'}, 'Segment_3': {'Label': 'Hepatic', 'Description': 'Tissue'}, 'Segment_4': {'Label': 'Portal', 'Description': 'Tissue'}, 'Segment_5': {'Label': 'Tumor_1', 'Description': 'Morphologically Altered Structure'}}

Now I would like to convert such a dcm file to a binary-segmentation file for use in one project–which means basically I need to combine all non-Tumor segmentation into one category as “non-Tumor”. I found it difficult to do that in pydicom as I can not extract the segmentation geometry information.
I think slicer python lib could be used for that purpose instead, but searching around I couldn’t find a good reference code.

Can someone help to give suggestions here?

Thanks for chatGPT, I am able to do a similar thing as below:

# 3d slicer implementation:
def valid_node(verbose:bool = False):
    segmentationNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSegmentationNode')
    segmentationNodes.InitTraversal()
    node = segmentationNodes.GetNextItemAsObject()

    if node.IsA('vtkMRMLSegmentationNode'):
        print(f"Found a vtkMRMLSegmentationNode: {node.GetName()}")
        segmentation = node.GetSegmentation()
        numberOfSegments = segmentation.GetNumberOfSegments()
        print(f"Number of Segments: {numberOfSegments}")
        
        # Loop over all segments
        if verbose:
            for i in range(numberOfSegments):
                segmentId = segmentation.GetNthSegmentID(i)
                segment = segmentation.GetSegment(segmentId)

                # Print basic segment details
                print(f"Segment ID: {segmentId}")
                print(f"Segment Name: {segment.GetName()}")
                #print(f"Segment Color: {segment.GetColor()}")  # This will give you the RGB color as a tuple
            
        #node = segmentationNodes.GetNextItemAsObject()
        return node
    
    else:
        print(f"Node is NOT a vtkMRMLSegmentationNode: {node.GetName()}")
        return None

However, there is one limitation that I have to do: 1). In 3D slicer import a segmentation data file to form a segmentationNode; 2). run this script in 3D slicer python console;

I have many segmentation files to proceed, which are located under different folders – it is not possible to import each data file and then execute this script.

To speak frankly, I do not fully understand the concept of the “segmentationNodes”. so 1st question: can someone help to explain this “node” concept?

my 2nd question is: Is that possible to setup a segmentationNodes by importing a particular segmentation data file, not in GUI but by using python code? I did not find such an example code yet.

The segmentation node concept is explained here: Image Segmentation — 3D Slicer documentation

I couldn’t fully understand your goal. Do you want to merge all segments into one? Or do you want to have two segments: tumor and non-tumor?

Can you please make a screenshot of the subject hierarchy (where we can see all segments) and explain what you want to do in that segmentation node? Thanks.

Thanks. The different data samples might have different labels. But at least it has 5 labels (4 non-Tumor and at least one Tumor)
here is the segment hierarchy, which shows 4 non-Tumor and 8 Tumor area.

my goal is to find a way that can isolate two areas: Tumor and non-Tumor.

I see, thank you. Do the segments have terminology set (double-click on color to see if you’re unsure), or do you want to only use the segment names?

Is the goal to merge all the tumor and all the non-tumor segments, thus having two segments?

Yes, all segmentation has “terminology set” as the below chart shows: BTW, this “terminology set” is a new concept for me as well, kindly send me some reference document to read if yuou have…

Is the goal to merge all the tumor and all the non-tumor segments, thus having two segments?
Yes, but actually may not be that straightforward, for example, because the Liver generally includes Tumor segmentation (overlap may exist)–I suspect I may need to exclude the “Tumor” segmentation from the Liver segmentaion…

Here is some information about terminologies (“set” was supposed to be a verb, sorry about the confusion): Terminologies — 3D Slicer documentation
(feel free to browser the documentation, as you can see it is useful, and finding certain information there is not hard)

Basically terminologies identify a segment’s content in a standard way, to avoid the need for deduce it from the segment name, and to minimize the possibility of human error.

You can get the terminology string from a segment like this:

terminologyStr = vtk.mutable('')
segment.GetTag(segment.GetTerminologyEntryTagName(), terminologyStr)

Then you can store the string for tumor, and do a comparison in your for loop for all the segments.

Merging the segments together into a single segment is probably best to use the Logical operator effect in Segment Editor.

Thanks, so the terminology is mostly some “standard string information” that can help to quickly identify what the segmentation belongs to? Here is one output by using this code, for one segmentation:

terminologyStr: Segmentation~SCT^49755003^Morphologically Altered Structure~SCT^4147007^Mass~^^~Segmentation~SCT^10200004^Liver~^^

I am still trying to probe the way to make the multi-segmentation a binary-segmentation. I think some voxel level overlapping check may be necessary.

BTW, do you have some advice on my original question: "Is that possible to setup a segmentationNodes by importing a particular segmentation data file, not in GUI but by using python code? "
what Ima doing is purely manual way, each time only dealing with one file–that is not acceptable for massive data files.

You can find examples for all the commonly needed operations in the Slicer script repository. For example, how to load a file as segmentation from nrrd and from DICOM.

@lassoan Thanks. This example works well, but it only accepts a folder path-- and it will load all dcm files in case there are multiple dcm files under the folder – Is there any API that can import a single dcm file, I did not find it in the help document.

def load_DICOM(dicomFilesDirectory):
    # instantiate a new DICOM browser
    slicer.util.selectModule("DICOM")
    dicomBrowser = slicer.modules.DICOMWidget.browserWidget.dicomBrowser
    # use dicomBrowser.ImportDirectoryCopy to make a copy of the files (useful for importing data from removable storage)
    dicomBrowser.importDirectory(dicomFilesDirectory, dicomBrowser.ImportDirectoryAddLink)
    # wait for import to finish before proceeding (optional, if removed then import runs in the background)
    dicomBrowser.waitForImportFinished()
    print(f"DCM folder {dicomFilesDirectory} load done!")

One more comment is: actually I do not really need to open a GUI and import a dcm file, what I need to do is setup a “segmentation node” as all segmentation operations need to be done on such a node-- does that really need to import dcm files? Can’t simply create something like a context, then operate physical dcm files one-by-one?

Once you import the data you can use the database API to find the studies of interest. Or you can sort the dicom data into distinct directories before importing, e.g. with this tool.