Skip to content

Commit 4731d4a

Browse files
committed
Update the multi-AI app. Tested working
Signed-off-by: M Q <[email protected]>
1 parent f0160ee commit 4731d4a

File tree

3 files changed

+78
-20
lines changed

3 files changed

+78
-20
lines changed

examples/apps/ai_multi_ai_app/app.py

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,22 @@
1111

1212
import logging
1313

14-
from monai.deploy.core import Application, env, resource
14+
# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.
15+
from pydicom.sr.codedict import codes
16+
17+
import monai.deploy.core as md
18+
from monai.deploy.core import Application, resource
1519
from monai.deploy.core.domain import Image
1620
from monai.deploy.core.io_type import IOType
1721
from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator
18-
from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator
22+
from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator, SegmentDescription
1923
from monai.deploy.operators.dicom_series_selector_operator import DICOMSeriesSelectorOperator
2024
from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator
21-
from monai.deploy.operators.monai_bundle_inference_operator import IOMapping, MonaiBundleInferenceOperator
22-
from monai.deploy.operators.stl_conversion_operator import STLConversionOperator # import as needed.
25+
from monai.deploy.operators.monai_bundle_inference_operator import (
26+
BundleConfigNames,
27+
IOMapping,
28+
MonaiBundleInferenceOperator,
29+
)
2330

2431

2532
@resource(cpu=1, gpu=1, memory="7Gi")
@@ -28,7 +35,7 @@
2835
# The Bundle Inference Operator as of now only requires torch>=1.10.2, and does not yet dynamically
2936
# parse the MONAI bundle to get the required pip package or ver on initialization, hence it does not set
3037
# its own @env decorator accordingly when the app is being packaged into a MONAI Package.
31-
@env(pip_packages=["torch>=1.12.0"])
38+
@md.env(pip_packages=["torch>=1.12.0"])
3239
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
3340
# The monai pkg is not required by this class, instead by the included operators.
3441
class App(Application):
@@ -50,7 +57,7 @@ class App(Application):
5057
<parent_fodler>
5158
├── pancreas_ct_dints
5259
│ └── model.ts
53-
└── spleen_model
60+
└── spleen_ct
5461
└── model.ts
5562
5663
Note:
@@ -91,20 +98,71 @@ def compose(self):
9198
# for it to install the packages in the MAP docker image.
9299
# Setting output IOType to DISK only works only for leaf operators, not the case in this example.
93100
# When multiple models/bundles are supported, create an inference operator for each.
101+
#
102+
# Pertinent MONAI Bundle:
103+
# https://github.com/Project-MONAI/model-zoo/tree/dev/models/spleen_ct_segmentation, v0.3.2
104+
# https://github.com/Project-MONAI/model-zoo/tree/dev/models/pancreas_ct_dints_segmentation, v0.3
105+
106+
config_names = BundleConfigNames(config_names=["inference"]) # Same as the default
107+
108+
# This is the inference operator for the spleen_model bundle. Note the model name.
94109
bundle_spleen_seg_op = MonaiBundleInferenceOperator(
95110
input_mapping=[IOMapping("image", Image, IOType.IN_MEMORY)],
96111
output_mapping=[IOMapping("pred", Image, IOType.IN_MEMORY)],
97-
model_name="spleen_model",
112+
bundle_config_names=config_names,
113+
model_name="spleen_ct",
98114
)
99115

100-
bundle_pancrea_seg_op = MonaiBundleInferenceOperator(
116+
# This is the inference operator for the pancreas_ct_dints bundle. Note the model name.
117+
bundle_pancreas_seg_op = MonaiBundleInferenceOperator(
101118
input_mapping=[IOMapping("image", Image, IOType.IN_MEMORY)],
102119
output_mapping=[IOMapping("pred", Image, IOType.IN_MEMORY)],
103120
model_name="pancreas_ct_dints",
104121
)
105122

106-
# Create DICOM Seg writer with segment label name in a string list
107-
dicom_seg_writer = DICOMSegmentationWriterOperator(seg_labels=["Spleen"])
123+
# Create DICOM Seg writer providing the required segment description for each segment with
124+
# the actual algorithm and the pertinent organ/tissue. The segment_label, algorithm_name,
125+
# and algorithm_version are of DICOM VR LO type, limited to 64 chars.
126+
# https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
127+
#
128+
# NOTE: Each generated DICOM Seg will be a dcm file with the name based on the SOP instance UID.
129+
130+
# Description for the Spleen seg, and the seg writer obj
131+
seg_descriptions_spleen = [
132+
SegmentDescription(
133+
segment_label="Spleen",
134+
segmented_property_category=codes.SCT.Organ,
135+
segmented_property_type=codes.SCT.Spleen,
136+
algorithm_name="volumetric (3D) segmentation of the spleen from CT image",
137+
algorithm_family=codes.DCM.ArtificialIntelligence,
138+
algorithm_version="0.3.2",
139+
)
140+
]
141+
142+
custom_tags_spleen = {"SeriesDescription": "AI Spleen Seg for research use only. Not for clinical use."}
143+
dicom_seg_writer_spleen = DICOMSegmentationWriterOperator(
144+
segment_descriptions=seg_descriptions_spleen, custom_tags=custom_tags_spleen
145+
)
146+
147+
# Description for the Pancreas seg, and the seg writer obj
148+
seg_descriptions_pancreas = [
149+
SegmentDescription(
150+
segment_label="Pancreas",
151+
segmented_property_category=codes.SCT.Organ,
152+
segmented_property_type=codes.SCT.Pancreas,
153+
algorithm_name="volumetric (3D) segmentation of the pancreas from CT image",
154+
algorithm_family=codes.DCM.ArtificialIntelligence,
155+
algorithm_version="0.3.0",
156+
)
157+
]
158+
custom_tags_pancreas = {"SeriesDescription": "AI Pancreas Seg for research use only. Not for clinical use."}
159+
160+
dicom_seg_writer_pancreas = DICOMSegmentationWriterOperator(
161+
segment_descriptions=seg_descriptions_pancreas, custom_tags=custom_tags_pancreas
162+
)
163+
164+
# NOTE: Sharp eyed readers can alreay see that the above instantiation of object can be simply parameterized.
165+
# Very true, but leaving them as if for easy reading. In fact the whole app can be parameterized for general use.
108166

109167
# Create the processing pipeline, by specifying the upstream and downstream operators, and
110168
# ensuring the output from the former matches the input of the latter, in both name and type.
@@ -116,21 +174,21 @@ def compose(self):
116174
# Feed the input image to all inference operators
117175
self.add_flow(series_to_vol_op, bundle_spleen_seg_op, {"image": "image"})
118176
# The Pancreas CT Seg bundle requires PyTorch 1.12.0 to avoid failure to load.
119-
self.add_flow(series_to_vol_op, bundle_pancrea_seg_op, {"image": "image"})
177+
self.add_flow(series_to_vol_op, bundle_pancreas_seg_op, {"image": "image"})
120178

121179
# Create DICOM Seg for one of the inference output
122180
# Note below the dicom_seg_writer requires two inputs, each coming from a upstream operator.
123181
self.add_flow(
124-
series_selector_op, dicom_seg_writer, {"study_selected_series_list": "study_selected_series_list"}
182+
series_selector_op, dicom_seg_writer_spleen, {"study_selected_series_list": "study_selected_series_list"}
125183
)
126-
self.add_flow(bundle_spleen_seg_op, dicom_seg_writer, {"pred": "seg_image"})
184+
self.add_flow(bundle_spleen_seg_op, dicom_seg_writer_spleen, {"pred": "seg_image"})
127185

128-
# Create the surface mesh STL conversion operator and add it to the app execution flow, if needed, by
129-
# uncommenting the following lines.
130-
stl_conversion_op = STLConversionOperator(
131-
output_file="stl/pancreas_tumor.stl",
186+
# Create DICOM Seg for one of the inference output
187+
# Note below the dicom_seg_writer requires two inputs, each coming from a upstream operator.
188+
self.add_flow(
189+
series_selector_op, dicom_seg_writer_pancreas, {"study_selected_series_list": "study_selected_series_list"}
132190
)
133-
self.add_flow(bundle_pancrea_seg_op, stl_conversion_op, {"pred": "image"})
191+
self.add_flow(bundle_pancreas_seg_op, dicom_seg_writer_pancreas, {"pred": "seg_image"})
134192

135193
logging.info(f"End {self.compose.__name__}")
136194

examples/apps/ai_pancrea_seg_app/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.
1515
from pydicom.sr.codedict import codes
1616

17+
import monai.deploy.core as md
1718
from monai.deploy.core import Application, resource
1819
from monai.deploy.core.domain import Image
1920
from monai.deploy.core.io_type import IOType
@@ -31,6 +32,7 @@
3132

3233

3334
@resource(cpu=1, gpu=1, memory="7Gi")
35+
@md.env(pip_packages=["torch>=1.12.0"])
3436
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
3537
# The monai pkg is not required by this class, instead by the included operators.
3638
class AIPancreasSegApp(Application):

examples/apps/ai_spleen_seg_app/app.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
MonaiBundleInferenceOperator,
2828
)
2929

30-
# from monai.deploy.operators.stl_conversion_operator import STLConversionOperator # import as needed.
31-
3230

3331
@resource(cpu=1, gpu=1, memory="7Gi")
3432
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.

0 commit comments

Comments
 (0)