1
- # Copyright 2021-2023 MONAI Consortium
1
+ # Copyright 2021-2025 MONAI Consortium
2
2
# Licensed under the Apache License, Version 2.0 (the "License");
3
3
# you may not use this file except in compliance with the License.
4
4
# You may obtain a copy of the License at
27
27
ImplicitVRLittleEndian , _ = optional_import ("pydicom.uid" , name = "ImplicitVRLittleEndian" )
28
28
Dataset , _ = optional_import ("pydicom.dataset" , name = "Dataset" )
29
29
FileDataset , _ = optional_import ("pydicom.dataset" , name = "FileDataset" )
30
+ PyDicomSequence , _ = optional_import ("pydicom.sequence" , name = "Sequence" )
30
31
sitk , _ = optional_import ("SimpleITK" )
31
32
codes , _ = optional_import ("pydicom.sr.codedict" , name = "codes" )
32
33
if TYPE_CHECKING :
39
40
from monai .deploy .core import ConditionType , Fragment , Image , Operator , OperatorSpec
40
41
from monai .deploy .core .domain .dicom_series import DICOMSeries
41
42
from monai .deploy .core .domain .dicom_series_selection import StudySelectedSeries
43
+ from monai .deploy .operators .dicom_utils import ModelInfo
42
44
43
45
44
46
class SegmentDescription :
@@ -183,6 +185,7 @@ def __init__(
183
185
* args ,
184
186
segment_descriptions : List [SegmentDescription ],
185
187
output_folder : Path ,
188
+ model_info : Optional [ModelInfo ] = None ,
186
189
custom_tags : Optional [Dict [str , str ]] = None ,
187
190
omit_empty_frames : bool = True ,
188
191
** kwargs ,
@@ -206,6 +209,7 @@ def __init__(
206
209
Object encapsulating the description of each segment present in the segmentation.
207
210
output_folder: Folder for file output, overridden by named input on compute.
208
211
Defaults to current working dir's child folder, output.
212
+ model_info (ModelInfo, optional): Object encapsulating model creator, name, version and UID.
209
213
custom_tags: Optional[Dict[str, str]], optional
210
214
Dictionary for setting custom DICOM tags using Keywords and str values only
211
215
omit_empty_frames: bool, optional
@@ -217,6 +221,7 @@ def __init__(
217
221
self ._custom_tags = custom_tags
218
222
self ._omit_empty_frames = omit_empty_frames
219
223
self .output_folder = output_folder if output_folder else DICOMSegmentationWriterOperator .DEFAULT_OUTPUT_FOLDER
224
+ self .model_info = model_info if model_info else ModelInfo ()
220
225
221
226
self .input_name_seg = "seg_image"
222
227
self .input_name_series = "study_selected_series_list"
@@ -356,6 +361,41 @@ def create_dicom_seg(self, image: np.ndarray, dicom_series: DICOMSeries, output_
356
361
# Best effort for now.
357
362
logging .warning (f"Tag { k } was not written, due to { ex } " )
358
363
364
+ # write model info
365
+ # code copied from write_common_modules method in monai.deploy.operators.dicom_utils
366
+
367
+ # Contributing Equipment Sequence
368
+ # The Creator shall describe each algorithm that was used to generate the results in the
369
+ # Contributing Equipment Sequence (0018,A001). Multiple items may be included. The Creator
370
+ # shall encode the following details in the Contributing Equipment Sequence:
371
+ # • Purpose of Reference Code Sequence (0040,A170) shall be (Newcode1, 99IHE, 1630 "Processing Algorithm")
372
+ # • Manufacturer (0008,0070)
373
+ # • Manufacturer’s Model Name (0008,1090)
374
+ # • Software Versions (0018,1020)
375
+ # • Device UID (0018,1002)
376
+
377
+ if self .model_info :
378
+ # First create the Purpose of Reference Code Sequence
379
+ seq_purpose_of_reference_code = PyDicomSequence ()
380
+ seg_purpose_of_reference_code = Dataset ()
381
+ seg_purpose_of_reference_code .CodeValue = "Newcode1"
382
+ seg_purpose_of_reference_code .CodingSchemeDesignator = "99IHE"
383
+ seg_purpose_of_reference_code .CodeMeaning = '"Processing Algorithm'
384
+ seq_purpose_of_reference_code .append (seg_purpose_of_reference_code )
385
+
386
+ seq_contributing_equipment = PyDicomSequence ()
387
+ seg_contributing_equipment = Dataset ()
388
+ seg_contributing_equipment .PurposeOfReferenceCodeSequence = seq_purpose_of_reference_code
389
+ # '(121014, DCM, “Device Observer Manufacturer")'
390
+ seg_contributing_equipment .Manufacturer = self .model_info .creator
391
+ # u'(121015, DCM, “Device Observer Model Name")'
392
+ seg_contributing_equipment .ManufacturerModelName = self .model_info .name
393
+ # u'(111003, DCM, “Algorithm Version")'
394
+ seg_contributing_equipment .SoftwareVersions = self .model_info .version
395
+ seg_contributing_equipment .DeviceUID = self .model_info .uid # u'(121012, DCM, “Device Observer UID")'
396
+ seq_contributing_equipment .append (seg_contributing_equipment )
397
+ seg .ContributingEquipmentSequence = seq_contributing_equipment
398
+
359
399
seg .save_as (output_path )
360
400
361
401
try :
0 commit comments