Writing a nrrd segmentation file in SimpleITK

Hi!

How can I save my connected components to a nrrd segmentation file that I can visualize in Slicer?

Thanks,

Diego

Hello Diego,

What have you tried and what problems did you run into? Do you have a minimal example to reproduce your problematic situation?

Thanks.

What you probably need is show advanced options when loading data in Slicer and select “Label map”. That option is on by default if the file name ends in -label (e.g. out-label.nrrd), so you can just drag-drop it into Slicer.

I have connected components in my image after:

cc = sitk.ConnectedComponentsImageFilter()
results = cc.Execute(im_input)

I want to save results as a nrrd segmentation file that I can visualize in Slicer.

Hello @Diego_C ,

Just save the results as an image:

sitk.WriteImage(results, 'cc_results.mha')

Then you just load this image and overlay onto the original (I usually use ITK-SNAP for quick visualizations as it loads faster than Slicer. When I need more functionality I go with Slicer).

Thanks @zivy I need to show the results in Slicer though. I was thinking of writing the segmentation metadata myself but I was wondering if there was a simpler way of doing it with SimpleITK?

Hi @Diego_C ,

Not sure what you mean by “segmentation metadata”. If you write the segmentation to disk you just open the saved file in Slicer.

If you want to programmatically open Slicer and display the image with overlay you will have to:

  1. Set Slicer as your viewing command (see ImageViewer).
  2. Create the color overlay image yourself (see visualization notebook), as the viewer expects a single image.

If you want to save segment name, color, standard DICOM terminology, etc. in the nrrd file then you can do that by adding metadata fields using itk::MetaDataDictionary and pass it on to the reader using SetMetaDataDictionary. Definition of seg.nrrd file custom metadata fields is available here.

4 Likes

Hey @zivy the nrrd files need to have a particular set of metadata headers so you can use them with the Segmentation Module in Slicer. I was wondering if there was a way to get this without having to coding it myself.

Thank you @lassoan this is what I was thinking I had to do. Thanks for the reference!

Cheers,

Diego

Hello Andras Lasso,
I want to save a segment label with its name, but I can not see how I do this using SimpleITK. The code to save my segment is:
writer = sitk.ImageFileWriter()
writer.Execute(label, segment.seg.nrrd’, True,0)
I need some help.

@lassoan this was pretty insightful, thank you!

Is there any nice example somewhere of assigning these correctly to a LabelMap with multiple labels? Especially when there are many labels and when SegmentN_Layer and SegmentN_LabelValue should be used.

Hello @ibro45,

Not exactly what you are looking for but closely related, an example of three labels saved to seg.nrrd format is available in the results visualization notebook, visual_comparison_of_segmentation function.

1 Like

Thank you so much @zivy, that really helped.

For future reference, I’ll explain what I did:

I needed to save overlapping segmentations with a particular name and color. I first collected the binary masks for each segmentation and then composed them using sitk.Compose. Then, I set the metadata for each segment in the composed mask. Code:

from SimpleITK import sitk


def set_individual_segment_metadata(mask, seg_num, name, color, tags=None):
    # `seg_num` starts from 0, but ID starts from 1
    mask.SetMetaData(f"Segment{seg_num}_ID", str(seg_num + 1))
    # A separate layer per label, allows overlapping segmentations.
    mask.SetMetaData(f"Segment{seg_num}_Layer", str(seg_num))
    # Each label is saved in a separate layer, so we just need to set the foreground of each to 1
    mask.SetMetaData(f"Segment{seg_num}_LabelValue", "1")

    mask.SetMetaData(f"Segment{seg_num}_Name", name)
    mask.SetMetaData(f"Segment{seg_num}_NameAutoGenerated", "0")

    mask.SetMetaData(f"Segment{seg_num}_Color", color)
    mask.SetMetaData(f"Segment{seg_num}_ColorAutoGenerated", "0")

    # Extent, as defined in https://github.com/InsightSoftwareConsortium/SimpleITK-Notebooks/blob/e805f171ef633575486b48377db4e0715d42f63d/Python/05_Results_Visualization.ipynb#L860
    mask.SetMetaData(
        f"Segment{seg_num}_Extent", 
        f"0 {mask.GetWidth()-1} 0 {mask.GetHeight()-1} 0 {mask.GetDepth()-1}"
    )

    if tags is not None:
        mask.SetMetaData(f"Segment{seg_num}_Tags", tags)

    return mask


# Combine all masks
mask = sitk.Compose([
    LA, LV, ...
])

# Set metadata per segment, make sure to apply in the same order as above
mask = set_individual_segment_metadata(mask, 0, "LA", "1.0 0.0 0.0")  # Red
mask = set_individual_segment_metadata(mask, 1, "LV", "0.0 1.0 0.0")  # Green
...

# Common metadata
mask.SetMetaData("Segmentation_MasterRepresentation", "Binary labelmap")
mask.SetMetaData("Segmentation_ReferenceImageExtentOffset", "0 0 0")
mask.SetMetaData("Segmentation_ContainedRepresentationNames", "Binary labelmap|")
3 Likes