neuropoly / slicer-manual-annotation

MIT License
2 stars 1 forks source link

Masks created in Slicer have different dimensinos than source image #21

Open valosekj opened 3 months ago

valosekj commented 3 months ago

I'm looking at sample segmentations for tSCI patients (PRAXIS) produced by @maxradx using 3D slicer, and I noticed that the label masks have different dimensions than the source image:

image

This is a problem. Ideally, the masks should have the same dimensions as the source image.

valosekj commented 3 months ago

Hey @maxradx! I was able to save a segmentation with the same dimensions, resolution, and orientation as the source image. I recorded a short video documenting it.

maxradx commented 3 months ago

Hi @valosekj ! Question of planning. Your short video corrects well the segmentation dimensions compared to the reference volume (now the dimensions fits). However, if we have 2 classes of segmentation (e.g. edema and hemorrhage), it saves it to one file (as shown in this image: white = presumed hemorrhage; grey = presumed edema). 1- That means it is not a binary file? 2- Is it better to have X files where X is the number of segmentation classes or is it better to have 1 file for all the segmentation classes as shown in this image?

image
valosekj commented 3 months ago

1- That means it is not a binary file?

Each class seems to have its integer value, e.g., white: value 1, gray: value 2. You can verify this in FSLeyes by placing a cursor on a given region and checking the value in the bottom right corner. If this is the case, then it is fine!

2- Is it better to have X files where X is the number of segmentation classes or is it better to have 1 file for all the segmentation classes as shown in this image?

Good question! For the PRAXIS database, we currently have multiple files (i.e., one file each for SC seg, edema and hemorrhage). This convention is compatible with nnUNet training and also, for example, with the sct_analyze_lesion function, which requires separate files for SC seg and lesion. However, splitting a multi-class file is easy (see example here), so if Slicer only supports saving a multi-class file, this is not a big problem.

maxradx commented 3 months ago

Ok thanks @valosekj ! I implemented a new function and it works to convert the segmentation already done to the same format. However, the function does not work when it is from a new case (save with the wrong dimensions) and I don't know why .... Probably there is something uncorrect in the segments that are loaded (but technically, the function should register them in the same geometry).... Will look more in details this week!

image

Function example:

# Save segments with same dimensions as reference volume node
    def save_same_dimensions(self):
        self.enter_function("save_same_dimensions")
        segmentation_node = self.get_segmentation_node()
        volume_node = self.get_volume_node()
        print("segmentation node name:", segmentation_node.GetName())
        print("volume node name", volume_node.GetName())

        # Get the active segment
        segments = self.get_active_segments(segmentation_node)
        # Iterate through segments and collect names
        segment_names = self.get_segment_names_list(segments)
        print("segment_names: ", segment_names)

        # Create a label_map_volume_node
        label_map_volume_node = slicer.mrmlScene.AddNewNodeByClass(
            "vtkMRMLLabelMapVolumeNode")
        slicer.modules.segmentations.logic().ExportAllSegmentsToLabelmapNode(
            segmentation_node, label_map_volume_node,
            slicer.vtkSegmentation.EXTENT_REFERENCE_GEOMETRY)

        segment_ids = vtk.vtkStringArray()
        for segment_name in segment_names:
            print("segment_name: ", segment_name)
            segment_id = segmentation_node.GetSegmentation(

            ).GetSegmentIdBySegmentName(
                segment_name)
            segment_ids.InsertNextValue(segment_id)

            slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentsToLabelmapNode(
                segmentation_node, segment_ids, label_map_volume_node,
                volume_node)

            # # Get the image data from the volume node
            # image_data = label_map_volume_node.GetImageData()
            #
            # # Get the dimensions of the volume
            # dimensions = image_data.GetDimensions()
            # print(f"label map dimensions: {dimensions}")

            # folder_path = (f"{self.output_folder}{os.sep}"
            #                f"versions{os.sep}")
            folder_path = "/Users/maximebouthillier/gitmax/data_confid/test_conversion_tsci"
            file_path = os.path.join(folder_path, segment_name)
            slicer.util.saveNode(label_map_volume_node, file_path)

Basic question: why there is an issue when I paste the code in the block of code (like it does not copy paste it correctly ...) ?

valosekj commented 3 months ago

I implemented a new function and it works to convert the segmentation already done to the same format. However, the function does not work when it is from a new case (save with the wrong dimensions) and I don't know why ....

Existing segmentations (annotated previously using FSLeyes or ITKsnap) already have the correct dimensions. Why? Because we created them as copies of the reference image (code here). The question is how 3D Slicer creates a new mask? Is it a copy of the reference image? (probably not; otherwise, dimensions would fit).

Basic question: why there is an issue when I paste the code in the block of code (like it does not copy paste it correctly ...) ?

I took the liberty to update your comment. To format text as a block of code, you can use backticks; see here.