Wrong segment IDs when exporting labelmap to file

Hello,

I am developing a scriptedLoadableModules and I am currently facing a small issue. So,

  1. I’am loading a segmentation and labelmap in which, for the sake of simplicity, I have three segmented regions (IDs: 3, 6 and 48)
  2. Next, let’s say I select region 48, and click “Remove”. (it will be removed)
  3. I have the following code, taken from the documentation, to save the current/modified labelmap (so without the region 48):

name = “current_results.nii.gz”
filePath = slicer.app.temporaryPath + ‘/’ + name
localLabelmap = slicer.mrmlScene.AddNewNodeByClass(‘vtkMRMLLabelMapVolumeNode’) slicer.modules.segmentations.logic().ExportAllSegmentsToLabelmapNode(self.segmentationVolumeNode, localLabelmap)
slicer.util.saveNode(localLabelmap, filePath)

  1. When I load current_results.nii.gz, I see two regions, which is good, but their IDs are 1 and 2. I see the same IDs when I open the image in ITKsnap. So, clearly the IDs are not being saved based on existing ones.

In logs file, I see the following:

[ERROR][VTK] 30.01.2020 00:20:20 [vtkLookupTable (00000255CE57F650)] (D:\D\S\Slicer-4100-build\VTK\Common\Core\vtkLookupTable.cxx:143) - Bad table range: [0, -1]
[ERROR][VTK] 30.01.2020 00:20:20 [vtkMRMLLabelMapVolumeDisplayNode (00000255D3176EE0)] (D:\D\S\Slicer-4100\Libs\MRML\Core\vtkMRMLLabelMapVolumeDisplayNode.cxx:150) - GetOutputImageData: Lookup table exists but empty!
[ERROR][VTK] 30.01.2020 00:20:20 [vtkLookupTable (00000255CE57FB90)] (D:\D\S\Slicer-4100-build\VTK\Common\Core\vtkLookupTable.cxx:143) - Bad table range: [0, -1]
[ERROR][VTK] 30.01.2020 00:20:20 [vtkMRMLLabelMapVolumeDisplayNode (00000255D3176EE0)] (D:\D\S\Slicer-4100\Libs\MRML\Core\vtkMRMLLabelMapVolumeDisplayNode.cxx:150) - GetOutputImageData: Lookup table exists but empty!
[WARNING][VTK] 30.01.2020 00:20:20 [] (unknown:0) - Generic Warning: In D:\D\S\Slicer-4100\Libs\MRML\Core\vtkDataFileFormatHelper.cxx, line 237
vtkDataFileFormatHelper::GetFileExtensionFromFormatString: please update deprecated extension-only format specifier to ‘File format name (.ext)’ format! Current format string: .nii.gz

I have no clue what those errors mean! I would really appreciate your help in telling me what am I doing wrong. How can I do the export keeping the same IDs as when loading?

Just so you know, I also tried (unsuccessfully) to do the export using this line of code:

slicer.modules.segmentations.logic().ExportVisibleSegmentsToLabelmapNode(self.segmentationVolumeNode, localLabelmap, self.labelmapVolumeNode)

Slicer keeps track of meaning of each label by using custom metadata fields in the file header.

In recent Slicer preview versions, original label values are preserved if you load a nrrd or nifti file as segmentation and save that segmentation in nrrd file.

Segments are relabeled if you export the segmentation to labelmap node, so if you want to preserve the original label values then avoid this operation.

Thanks for your input.

But, then how can I save the image with changes done through slicer? (Of course while keeping the original labels)

If you want to preserve label values then load the files as segmentation and save the file as segmentation. Slicer saves segmentation in a .nrrd file, which can be loaded by any other imaging software (they will not use the additional information in the custom fields, such as segment name and color, but that should not matter).

Note that you need to use a recent Slicer preview release. Slicer-4.10 always saved segmentation as a 4D volume and so label values were not preserved, but recent Slicer Preview releases will keep the segmentation in a 3D volume (and the original label values) if you don’t have overlapping segments.

Can you write a bit about your workflow? What do you do with the modified segmentation file? How do you currently know which label value correspond to which segment?

  • So when you say “If you want to preserve label values then load the files as segmentation and save the file as segmentation”, how can do the save through Python? Can you show me an example please !
  • I will try, then, to use version 4.11 to see if it works! I will let you know.
  • Briefly, I have an application with some sort of catalogs/atlases of different organs (brain, heart, …), for which I know (using my automatic segmentation method) how to segment all regions when a new image is given. However, it is possible that my catalogs are not complete (some regions are missing) or not accurate, that’s why I am using Slicer (SegmentationEditor) to improve or perform a segmentation for a new region. Therefore, I need to extract/export/save the image after modification in slicer, so I can update my catalog. If label values have changed (which is my problem right now), then I’ll lose track on my side. Hope it’s clear!

You can use slicer.util.saveNode() to save the segmentation node to file.

I see. Probably you then have metadata files somewhere that store which label correspond to which segment, which is a reasonable solution, as it is very simple. Is that the case? Is there a standard format you follow?

In Slicer .seg.nrrd file format, we store this metadata in the same file as the image data, which is very robust, but it may be slightly inconvenient to read/write nrrd header to access this data. It is also important to note that Slicer uses standard terminologies (DICOM, and soon TA2) to identify segment contents to make the content really portable (avoid all issues around spelling, names in different languages, etc.).

In scope of OpenAnatomy project, we are working on additional export options (such as glTF+json) to create portable and interoperable atlases.

So here are my findings:

  1. I tried version 4.11, saving segmentation to nii file, and I got exactly the same behavior, original labels were lost. Code used:
  1. Next, I tried saving with the same way to nrrd file (both versions 4.10 and 4.11). The header files did not have information about original labels.
  2. Then, I changed the way I am saving the node using this code:

myStorageNode = self.segmentationVolumeNode.CreateDefaultStorageNode()
myStorageNode.SetFileName(filePath)
myStorageNode.WriteData(self.segmentationVolumeNode)

3.a. If I save to .nii file, labels are lost.
3.b. While saving to nrrd, I get the following:


Which is exactly what I am looking for. The original IDs are stored in metadata. The problem, however, is that the pixels are not labeled with those IDs. In fact, I only have two IDs in the whole image, 0 and 1. All segments are labeled 1 and the rest is 0. I reproduced this in ITK-Snap.

Basically, I still have the problem! I will try DICOM and see.

I am using a graph-based approach to store segments information, where I have there IDs and additional features such spacial relationships and many others…

Thanks for sharing OpenAnatomy! It is similar to what I am working on.

ExportAllSegmentsToLabelmapNode assign lables from 0 to (n-1). Same as when you export to labelmap using GUI. If you want to preserve original label values then don’t use this method.

Since you have called ExportAllSegmentsToLabelmapNode before, it does not matter what file format you saved into after that.

It is very hard to save custom fields to nifti file header, therefore we do not support writing segmentation to nifti file.

Slicer is developed rapidly. The 4.11 version that you tried may be several months old, which is old. Try with the most recent preview release and you’ll find that original label values are preserved in the file.

This is a 4D volume (dimension: 4). Earlier Slicer versions always created 4D volume (each segment is stored as a separate 3D volume). This is what you see in the file header above.

Users had difficulty using this file format with third-party software that could only handle 3D volumes (and it also consumed more memory than it was necessary), so we recently changed Slicer so that it can store non-overlapping segments in a single 3D volume. We still support 4D segmentation volumes and use it when segments overlap (we use multiple “layers” of 3D volumes).

For clinical applications, DICOM is very good (and unavoidable anyway). If you install Quantitative Reporting extension then you can export segmentation to DICOM segmentation object (you can also export to DICOM RT structure set but that’s a terrible format, so I would not recommend to use that). You can set standard terminology by double-clicking the color swatch in segment list. Slicer is shipped with 2 terminologies (both are based on DICOM SNOMED CT terms, one contains a large list, the other contains only those terms that are listed in 3D Slicer’s GenericAnatomyColors colormap; but you can add your own terminology list).

Great! Make sure you coordinate with them to avoid parallel efforts.

They are working closely with leading anatomist groups worldwide (for example, they are going to release TA2 soon), collaborating with several groups who produce atlases, building tools to organize terminologies, creating graphs/hierarchies to organize them, provide various filtering, searching, tagging mechanisms in web and desktop applications (including authoring and export tools in Slicer), etc.

I’m sure they would be happy to hear about your efforts and there would be lots of collaboration opportunities.

@mhalle do you have anything to add?

Just so we can close this thread, the easiest solution that worked for me was to parse manually the nrrd file and generate my expected nii one. In a nutshell:

  1. I am saving the segmentation into nrrd using:

myStorageNode = self.segmentationVolumeNode.CreateDefaultStorageNode()
myStorageNode.SetFileName(filePath)
myStorageNode.WriteData(self.segmentationVolumeNode)

  1. I parse the nrrd and convert to nii (using pynrrd and nibabel). I count on nrrd metadata to get the original labels and set them in my nii.

This is the cheapest solution for me so far and it does the job. However, soon I’ll be shifting to a newer version of slicer and adding more functionalities especially supporting DICOM format.

Thank you.

2 Likes