10
10
# limitations under the License.
11
11
12
12
import logging
13
+ from pathlib import Path
13
14
14
15
# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.
15
16
from pydicom .sr .codedict import codes
16
17
17
- from monai .deploy .core import Application , resource
18
+ from monai .deploy .conditions import CountCondition
19
+ from monai .deploy .core import AppContext , Application
18
20
from monai .deploy .core .domain import Image
19
21
from monai .deploy .core .io_type import IOType
22
+ from monai .deploy .logger import load_env_log_level
20
23
from monai .deploy .operators .dicom_data_loader_operator import DICOMDataLoaderOperator
21
24
from monai .deploy .operators .dicom_seg_writer_operator import DICOMSegmentationWriterOperator , SegmentDescription
22
25
from monai .deploy .operators .dicom_series_selector_operator import DICOMSeriesSelectorOperator
26
29
IOMapping ,
27
30
MonaiBundleInferenceOperator ,
28
31
)
32
+ from monai .deploy .operators .stl_conversion_operator import STLConversionOperator
29
33
30
34
31
- @resource (cpu = 1 , gpu = 1 , memory = "7Gi" )
35
+ # @resource(cpu=1, gpu=1, memory="7Gi")
32
36
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
33
37
# The monai pkg is not required by this class, instead by the included operators.
34
38
class AISpleenSegApp (Application ):
@@ -48,10 +52,17 @@ def compose(self):
48
52
49
53
logging .info (f"Begin { self .compose .__name__ } " )
50
54
55
+ app_context = AppContext ({}) # Let it figure out all the attributes without overriding
56
+ app_input_path = Path (app_context .input_path )
57
+ app_output_path = Path (app_context .output_path )
58
+ model_path = Path (app_context .model_path )
59
+
51
60
# Create the custom operator(s) as well as SDK built-in operator(s).
52
- study_loader_op = DICOMDataLoaderOperator ()
53
- series_selector_op = DICOMSeriesSelectorOperator (Sample_Rules_Text )
54
- series_to_vol_op = DICOMSeriesToVolumeOperator ()
61
+ study_loader_op = DICOMDataLoaderOperator (
62
+ self , CountCondition (self , 1 ), input_folder = app_input_path , name = "study_loader_op"
63
+ )
64
+ series_selector_op = DICOMSeriesSelectorOperator (self , rules = Sample_Rules_Text , name = "series_selector_op" )
65
+ series_to_vol_op = DICOMSeriesToVolumeOperator (self , name = "series_to_vol_op" )
55
66
56
67
# Create the inference operator that supports MONAI Bundle and automates the inference.
57
68
# The IOMapping labels match the input and prediction keys in the pre and post processing.
@@ -68,9 +79,12 @@ def compose(self):
68
79
config_names = BundleConfigNames (config_names = ["inference" ]) # Same as the default
69
80
70
81
bundle_spleen_seg_op = MonaiBundleInferenceOperator (
82
+ self ,
71
83
input_mapping = [IOMapping ("image" , Image , IOType .IN_MEMORY )],
72
84
output_mapping = [IOMapping ("pred" , Image , IOType .IN_MEMORY )],
73
85
bundle_config_names = config_names ,
86
+ bundle_path = model_path ,
87
+ name = "bundle_spleen_seg_op" ,
74
88
)
75
89
76
90
# Create DICOM Seg writer providing the required segment description for each segment with
@@ -91,25 +105,31 @@ def compose(self):
91
105
custom_tags = {"SeriesDescription" : "AI generated Seg, not for clinical use." }
92
106
93
107
dicom_seg_writer = DICOMSegmentationWriterOperator (
94
- segment_descriptions = segment_descriptions , custom_tags = custom_tags
108
+ self ,
109
+ segment_descriptions = segment_descriptions ,
110
+ custom_tags = custom_tags ,
111
+ output_folder = app_output_path ,
112
+ name = "dicom_seg_writer" ,
95
113
)
96
114
97
115
# Create the processing pipeline, by specifying the source and destination operators, and
98
116
# ensuring the output from the former matches the input of the latter, in both name and type.
99
- self .add_flow (study_loader_op , series_selector_op , {"dicom_study_list" : "dicom_study_list" })
117
+ self .add_flow (study_loader_op , series_selector_op , {( "dicom_study_list" , "dicom_study_list" ) })
100
118
self .add_flow (
101
- series_selector_op , series_to_vol_op , {"study_selected_series_list" : "study_selected_series_list" }
119
+ series_selector_op , series_to_vol_op , {( "study_selected_series_list" , "study_selected_series_list" ) }
102
120
)
103
- self .add_flow (series_to_vol_op , bundle_spleen_seg_op , {"image" : "image" })
121
+ self .add_flow (series_to_vol_op , bundle_spleen_seg_op , {( "image" , "image" ) })
104
122
# Note below the dicom_seg_writer requires two inputs, each coming from a source operator.
105
123
self .add_flow (
106
- series_selector_op , dicom_seg_writer , {"study_selected_series_list" : "study_selected_series_list" }
124
+ series_selector_op , dicom_seg_writer , {( "study_selected_series_list" , "study_selected_series_list" ) }
107
125
)
108
- self .add_flow (bundle_spleen_seg_op , dicom_seg_writer , {"pred" : "seg_image" })
126
+ self .add_flow (bundle_spleen_seg_op , dicom_seg_writer , {( "pred" , "seg_image" ) })
109
127
# Create the surface mesh STL conversion operator and add it to the app execution flow, if needed, by
110
128
# uncommenting the following couple lines.
111
- # stl_conversion_op = STLConversionOperator(output_file="stl/spleen.stl")
112
- # self.add_flow(bundle_spleen_seg_op, stl_conversion_op, {"pred": "image"})
129
+ stl_conversion_op = STLConversionOperator (
130
+ self , output_file = app_output_path .joinpath ("stl/spleen.stl" ), name = "stl_conversion_op"
131
+ )
132
+ self .add_flow (bundle_spleen_seg_op , stl_conversion_op , {("pred" , "image" )})
113
133
114
134
logging .info (f"End { self .compose .__name__ } " )
115
135
@@ -140,5 +160,5 @@ def compose(self):
140
160
# e.g.
141
161
# monai-deploy exec app.py -i input -m model/model.ts
142
162
#
143
- logging . basicConfig ( level = logging . DEBUG )
144
- app_instance = AISpleenSegApp (do_run = True )
163
+ load_env_log_level ( )
164
+ AISpleenSegApp (). run ( )
0 commit comments