Skip to content

DICOM Seg Writer Op Tag Writing Updates #533

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion monai/deploy/operators/dicom_seg_writer_operator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021-2023 MONAI Consortium
# Copyright 2021-2025 MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
Expand Down Expand Up @@ -27,6 +27,7 @@
ImplicitVRLittleEndian, _ = optional_import("pydicom.uid", name="ImplicitVRLittleEndian")
Dataset, _ = optional_import("pydicom.dataset", name="Dataset")
FileDataset, _ = optional_import("pydicom.dataset", name="FileDataset")
PyDicomSequence, _ = optional_import("pydicom.sequence", name="Sequence")
sitk, _ = optional_import("SimpleITK")
codes, _ = optional_import("pydicom.sr.codedict", name="codes")
if TYPE_CHECKING:
Expand All @@ -39,6 +40,7 @@
from monai.deploy.core import ConditionType, Fragment, Image, Operator, OperatorSpec
from monai.deploy.core.domain.dicom_series import DICOMSeries
from monai.deploy.core.domain.dicom_series_selection import StudySelectedSeries
from monai.deploy.operators.dicom_utils import ModelInfo


class SegmentDescription:
Expand Down Expand Up @@ -183,6 +185,7 @@ def __init__(
*args,
segment_descriptions: List[SegmentDescription],
output_folder: Path,
model_info: Optional[ModelInfo] = None,
custom_tags: Optional[Dict[str, str]] = None,
omit_empty_frames: bool = True,
**kwargs,
Expand All @@ -206,6 +209,7 @@ def __init__(
Object encapsulating the description of each segment present in the segmentation.
output_folder: Folder for file output, overridden by named input on compute.
Defaults to current working dir's child folder, output.
model_info (ModelInfo, optional): Object encapsulating model creator, name, version and UID.
custom_tags: Optional[Dict[str, str]], optional
Dictionary for setting custom DICOM tags using Keywords and str values only
omit_empty_frames: bool, optional
Expand All @@ -217,6 +221,7 @@ def __init__(
self._custom_tags = custom_tags
self._omit_empty_frames = omit_empty_frames
self.output_folder = output_folder if output_folder else DICOMSegmentationWriterOperator.DEFAULT_OUTPUT_FOLDER
self.model_info = model_info if model_info else ModelInfo()

self.input_name_seg = "seg_image"
self.input_name_series = "study_selected_series_list"
Expand Down Expand Up @@ -356,6 +361,41 @@ def create_dicom_seg(self, image: np.ndarray, dicom_series: DICOMSeries, output_
# Best effort for now.
logging.warning(f"Tag {k} was not written, due to {ex}")

# write model info
# code copied from write_common_modules method in monai.deploy.operators.dicom_utils

# Contributing Equipment Sequence
# The Creator shall describe each algorithm that was used to generate the results in the
# Contributing Equipment Sequence (0018,A001). Multiple items may be included. The Creator
# shall encode the following details in the Contributing Equipment Sequence:
# • Purpose of Reference Code Sequence (0040,A170) shall be (Newcode1, 99IHE, 1630 "Processing Algorithm")
# • Manufacturer (0008,0070)
# • Manufacturer’s Model Name (0008,1090)
# • Software Versions (0018,1020)
# • Device UID (0018,1002)

if self.model_info:
# First create the Purpose of Reference Code Sequence
seq_purpose_of_reference_code = PyDicomSequence()
seg_purpose_of_reference_code = Dataset()
seg_purpose_of_reference_code.CodeValue = "Newcode1"
seg_purpose_of_reference_code.CodingSchemeDesignator = "99IHE"
seg_purpose_of_reference_code.CodeMeaning = '"Processing Algorithm'
seq_purpose_of_reference_code.append(seg_purpose_of_reference_code)

seq_contributing_equipment = PyDicomSequence()
seg_contributing_equipment = Dataset()
seg_contributing_equipment.PurposeOfReferenceCodeSequence = seq_purpose_of_reference_code
# '(121014, DCM, “Device Observer Manufacturer")'
seg_contributing_equipment.Manufacturer = self.model_info.creator
# u'(121015, DCM, “Device Observer Model Name")'
seg_contributing_equipment.ManufacturerModelName = self.model_info.name
# u'(111003, DCM, “Algorithm Version")'
seg_contributing_equipment.SoftwareVersions = self.model_info.version
seg_contributing_equipment.DeviceUID = self.model_info.uid # u'(121012, DCM, “Device Observer UID")'
seq_contributing_equipment.append(seg_contributing_equipment)
seg.ContributingEquipmentSequence = seq_contributing_equipment

seg.save_as(output_path)

try:
Expand Down