Skip to content

[IMP] Duplicate Input DICOM Spatial Coordinates #532

Closed
@bluna301

Description

@bluna301

Is your enhancement request related to a problem? Please describe.
When running the MONAI CT TotalSegmentator MAP pipeline pythonically on an internal hospital study, the following error is produced by highdicom via the DICOMSegmentationWriterOperator:

(ct-totalsegmentator) bluna301@EW22-04661:~/ct-totalsegmentator-map$ ./scripts/model_run.sh
[info] [fragment.cpp:599] Loading extensions from configs...
[2025-04-02 09:44:27,202] [INFO] (root) - Parsed args: Namespace(log_level=None, input=PosixPath('/home/bluna301/ct-totalsegmentator-map/dicom_data/duplicate'), output=PosixPath('/home/bluna301/ct-totalsegmentator-map/output'), model=PosixPath('/home/bluna301/ct-totalsegmentator-map/model/model.ts'), workdir=None, argv=['my_app', '-i', '/home/bluna301/ct-totalsegmentator-map/dicom_data/duplicate', '-o', '/home/bluna301/ct-totalsegmentator-map/output', '-m', '/home/bluna301/ct-totalsegmentator-map/model/model.ts'])
[2025-04-02 09:44:27,202] [INFO] (root) - AppContext object: AppContext(input_path=/home/bluna301/ct-totalsegmentator-map/dicom_data/duplicate, output_path=/home/bluna301/ct-totalsegmentator-map/output, model_path=/home/bluna301/ct-totalsegmentator-map/model/model.ts, workdir=)
[2025-04-02 09:44:27,219] [INFO] (root) - End compose
[info] [gxf_executor.cpp:264] Creating context
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'input_folder'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'dicom_study_list'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'study_selected_series_list'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'image'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'study_selected_series_list'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'text'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'output_folder'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'study_selected_series_list'
[info] [gxf_executor.cpp:1797] creating input IOSpec named 'seg_image'
[info] [gxf_executor.cpp:2208] Activating Graph...
[info] [gxf_executor.cpp:2238] Running Graph...
[info] [gxf_executor.cpp:2240] Waiting for completion...
[info] [greedy_scheduler.cpp:191] Scheduling 6 entities
[2025-04-02 09:44:27,347] [INFO] (monai.deploy.operators.dicom_data_loader_operator.DICOMDataLoaderOperator) - No or invalid input path from the optional input port: None
[2025-04-02 09:44:27,812] [INFO] (root) - Finding series for Selection named: Standard Axial CT Series
[2025-04-02 09:44:27,813] [INFO] (root) - Searching study, : 1.2.840.113619.2.405.3.185216268.497.1730719949.224
  # of series: 1
[2025-04-02 09:44:27,813] [INFO] (root) - Working on series, instance UID: 1.2.840.113619.2.405.3.185216268.497.1730719949.233
[2025-04-02 09:44:27,813] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2025-04-02 09:44:27,813] [INFO] (root) -     Series attribute StudyDescription value: NM FDG PET CT SKULL TO MID THIGH AND CT N/C WITH CONTRAST
[2025-04-02 09:44:27,813] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2025-04-02 09:44:27,813] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2025-04-02 09:44:27,813] [INFO] (root) -     Series attribute Modality value: CT
[2025-04-02 09:44:27,813] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2025-04-02 09:44:27,813] [INFO] (root) - On attribute: 'ImageType' to match value: ['PRIMARY', 'ORIGINAL', 'AXIAL']
[2025-04-02 09:44:27,813] [INFO] (root) -     Series attribute ImageType value: None
[2025-04-02 09:44:27,813] [INFO] (root) - On attribute: 'SliceThickness' to match value: [2, 5]
[2025-04-02 09:44:27,813] [INFO] (root) -     Series attribute SliceThickness value: None
[2025-04-02 09:44:27,813] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(?i)^(?!.*(cor|sag)).*$'
[2025-04-02 09:44:27,814] [INFO] (root) -     Series attribute SeriesDescription value: LDCT
[2025-04-02 09:44:27,814] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2025-04-02 09:44:27,814] [INFO] (root) - Selected Series, UID: 1.2.840.113619.2.405.3.185216268.497.1730719949.233
[2025-04-02 09:44:27,814] [INFO] (root) - Series Selection finalized.
[2025-04-02 09:44:27,814] [INFO] (root) - Series Description of selected DICOM Series for inference: LDCT
[2025-04-02 09:44:27,814] [INFO] (root) - Series Instance UID of selected DICOM Series for inference: 1.2.840.113619.2.405.3.185216268.497.1730719949.233
[2025-04-02 09:44:35,110] [INFO] (ct_totalseg_operator.CTTotalSegOperator) - TorchScript model detected
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Converted Image object metadata:
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - SeriesInstanceUID: 1.2.840.113619.2.405.3.185216268.497.1730719949.233, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - SeriesDate: 20241111, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - SeriesTime: 143622.000, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Modality: CT, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - SeriesDescription: LDCT, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - PatientPosition: FFS, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - SeriesNumber: 2, type <class 'int'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - row_pixel_spacing: 0.976562, type <class 'float'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - col_pixel_spacing: 0.976562, type <class 'float'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - depth_pixel_spacing: 3.75, type <class 'float'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - row_direction_cosine: [1.0, 0.0, 0.0], type <class 'list'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - col_direction_cosine: [0.0, 1.0, 0.0], type <class 'list'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - depth_direction_cosine: [0.0, 0.0, 1.0], type <class 'list'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - dicom_affine_transform: [[ 9.76562000e-01  0.00000000e+00  0.00000000e+00 -2.50000000e+02]
 [ 0.00000000e+00  9.76562000e-01  0.00000000e+00 -2.50000000e+02]
 [ 0.00000000e+00  0.00000000e+00  3.72631833e+00 -1.32750000e+03]
 [ 0.00000000e+00  0.00000000e+00  0.00000000e+00  1.00000000e+00]], type <class 'numpy.ndarray'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - nifti_affine_transform: [[-9.76562000e-01 -0.00000000e+00 -0.00000000e+00  2.50000000e+02]
 [-0.00000000e+00 -9.76562000e-01 -0.00000000e+00  2.50000000e+02]
 [ 0.00000000e+00  0.00000000e+00  3.72631833e+00 -1.32750000e+03]
 [ 0.00000000e+00  0.00000000e+00  0.00000000e+00  1.00000000e+00]], type <class 'numpy.ndarray'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - StudyInstanceUID: 1.2.840.113619.2.405.3.185216268.497.1730719949.224, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - StudyID: 5675, type <class 'str'>
[2025-04-02 09:44:35,111] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - StudyDate: 20241111, type <class 'str'>
[2025-04-02 09:44:35,112] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - StudyTime: 143203.000, type <class 'str'>
[2025-04-02 09:44:35,112] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - StudyDescription: NM FDG PET CT SKULL TO MID THIGH AND CT N/C WITH CONTRAST, type <class 'str'>
[2025-04-02 09:44:35,112] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - AccessionNumber: 11144480, type <class 'str'>
[2025-04-02 09:44:35,112] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - selection_name: Standard Axial CT Series, type <class 'str'>
[2025-04-02 09:44:39,779] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Input of <class 'monai.data.meta_tensor.MetaTensor'> shape: torch.Size([1, 1, 334, 334, 774])
[2025-04-02 09:53:17,500] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Post transform length/batch size of output: 1
[2025-04-02 09:53:17,827] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Post transform pixel spacings for pred: tensor([0.9766, 0.9766, 3.7263], dtype=torch.float64)
[2025-04-02 09:53:17,828] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Post transform pred of <class 'numpy.ndarray'> shape: (1, 512, 512, 312)
[2025-04-02 09:53:17,854] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Output Seg image numpy array of type <class 'numpy.ndarray'> shape: (312, 512, 512)
[2025-04-02 09:53:17,861] [INFO] (monai_seg_inference_operator.MonaiSegInferenceOperator) - Output Seg image pixel max value: 104
[2025-04-02 09:53:18,488] [INFO] (root) - Finished writing DICOM instance to file /home/bluna301/ct-totalsegmentator-map/output/SR/1.2.826.0.1.3680043.8.498.47870571337236747815607421787958144492.dcm
[2025-04-02 09:53:18,496] [INFO] (monai.deploy.operators.dicom_text_sr_writer_operator.DICOMTextSRWriterOperator) - DICOM SOP instance saved in /home/bluna301/ct-totalsegmentator-map/output/SR/1.2.826.0.1.3680043.8.498.47870571337236747815607421787958144492.dcm
[error] [gxf_wrapper.cpp:100] Exception occurred for operator: 'dicom_seg_writer' - ValueError: Input image/frame positions are not unique according to the Dimension Index Pointers. The generated segmentation would be ambiguous. Ensure that source images/frames have distinct locations.

At:
  /home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/highdicom/seg/content.py(668): get_index_values
  /home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/highdicom/seg/sop.py(1829): __init__
  /home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/monai/deploy/operators/dicom_seg_writer_operator.py(318): create_dicom_seg
  /home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/monai/deploy/operators/dicom_seg_writer_operator.py(301): process_images
  /home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/monai/deploy/operators/dicom_seg_writer_operator.py(280): compute

[error] [entity_executor.cpp:596] Failed to tick codelet dicom_seg_writer in entity: dicom_seg_writer code: GXF_FAILURE
[warning] [greedy_scheduler.cpp:243] Error while executing entity 41 named 'dicom_seg_writer': GXF_FAILURE
[info] [greedy_scheduler.cpp:401] Scheduler finished.
[error] [program.cpp:580] wait failed. Deactivating...
[error] [runtime.cpp:1649] Graph wait failed with error: GXF_FAILURE
[warning] [gxf_executor.cpp:2241] GXF call GxfGraphWait(context) in line 2241 of file /workspace/holoscan-sdk/src/core/executors/gxf/gxf_executor.cpp failed with 'GXF_FAILURE' (1)
[info] [gxf_executor.cpp:2251] Graph execution finished.
[error] [gxf_executor.cpp:2259] Graph execution error: GXF_FAILURE
Traceback (most recent call last):
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/bluna301/ct-totalsegmentator-map/my_app/__main__.py", line 25, in <module>
    CTTotalSegmentatorApp().run()
  File "/home/bluna301/ct-totalsegmentator-map/my_app/app.py", line 63, in run
    super().run(*args, **kwargs)
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/monai/deploy/operators/dicom_seg_writer_operator.py", line 280, in compute
    self.process_images(seg_image, study_selected_series_list, output_folder)
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/monai/deploy/operators/dicom_seg_writer_operator.py", line 301, in process_images
    self.create_dicom_seg(seg_image_numpy, dicom_series, output_dir)
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/monai/deploy/operators/dicom_seg_writer_operator.py", line 318, in create_dicom_seg
    seg = hd.seg.Segmentation(
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/highdicom/seg/sop.py", line 1829, in __init__
    self.DimensionIndexSequence.get_index_values(
  File "/home/bluna301/miniconda3/envs/ct-totalsegmentator/lib/python3.9/site-packages/highdicom/seg/content.py", line 668, in get_index_values
    raise ValueError(
ValueError: Input image/frame positions are not unique according to the Dimension Index Pointers. The generated segmentation would be ambiguous. Ensure that source images/frames have distinct locations.

Through parsing the DICOMSegmentationWriterOperator and the highdicom source code, it was determined that this error is caused by the input DICOM Series having multiple SOP instances with duplicate spatial coordinates (i.e. SliceLocation and ImagePositionPatient tags).

For this specific case, two SOP instances (Image #'s 156 and 157) have the exact same spatial coordinates:

  • SliceLocation: -746.25
  • ImagePositionPatient: [-250.000, -250.000, -746.250]

While they have the same spatial coordinates, the images do not appear visually identical (although they are very similar):

Image 156:
Image

Image 157:
Image

One explanation for the duplicate slices could be that this DICOM Series is a whole-body CT scan (312 total SOP instances) that has multiple AcquisitionNumber values (1-3) throughout the series (see below when DICOM Series is loaded in 3D Slicer). The duplicate slices occur at the most inferior slice of AcquisitionNumber = 2, and the most superior slice of AcquisitionNumber = 3.

Image

Describe the solution you'd like
Ideally, there would be a way within MONAI Deploy App SDK to account for slices with duplicate spatial coordinates, perhaps by deleting one of the duplicate instances or by "skipping" that slice location and deleting both duplicates. Considering the DICOMSegmentationWriterOperator depends on highdicom, it may be easiest to employ a fix within the DICOMSeriesToVolumeOperator, perhaps within the prepare_series method where there is already existing functionality to remove slices.

Describe alternatives you've considered
Updating highdicom to account for duplicate input slices.

Additional context
Additional background on slices with duplicate spatial coordinates.

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions