Skip to content

Commit f0160ee

Browse files
committed
Add the app for Pancreas bundle to repo, idential to the Spleen one except class and organ names
Signed-off-by: M Q <[email protected]>
1 parent bfbbd4b commit f0160ee

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import os
2+
import sys
3+
4+
_current_dir = os.path.abspath(os.path.dirname(__file__))
5+
if sys.path and os.path.abspath(sys.path[0]) != _current_dir:
6+
sys.path.insert(0, _current_dir)
7+
del _current_dir
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from app import AISpleenSegApp
2+
3+
if __name__ == "__main__":
4+
AISpleenSegApp(do_run=True)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Copyright 2021-2022 MONAI Consortium
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
11+
12+
import logging
13+
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+
from monai.deploy.core import Application, resource
18+
from monai.deploy.core.domain import Image
19+
from monai.deploy.core.io_type import IOType
20+
from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator
21+
from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator, SegmentDescription
22+
from monai.deploy.operators.dicom_series_selector_operator import DICOMSeriesSelectorOperator
23+
from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator
24+
from monai.deploy.operators.monai_bundle_inference_operator import (
25+
BundleConfigNames,
26+
IOMapping,
27+
MonaiBundleInferenceOperator,
28+
)
29+
30+
# from monai.deploy.operators.stl_conversion_operator import STLConversionOperator # import as needed.
31+
32+
33+
@resource(cpu=1, gpu=1, memory="7Gi")
34+
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
35+
# The monai pkg is not required by this class, instead by the included operators.
36+
class AIPancreasSegApp(Application):
37+
def __init__(self, *args, **kwargs):
38+
"""Creates an application instance."""
39+
self._logger = logging.getLogger("{}.{}".format(__name__, type(self).__name__))
40+
super().__init__(*args, **kwargs)
41+
42+
def run(self, *args, **kwargs):
43+
# This method calls the base class to run. Can be omitted if simply calling through.
44+
self._logger.info(f"Begin {self.run.__name__}")
45+
super().run(*args, **kwargs)
46+
self._logger.info(f"End {self.run.__name__}")
47+
48+
def compose(self):
49+
"""Creates the app specific operators and chain them up in the processing DAG."""
50+
51+
logging.info(f"Begin {self.compose.__name__}")
52+
53+
# Create the custom operator(s) as well as SDK built-in operator(s).
54+
study_loader_op = DICOMDataLoaderOperator()
55+
series_selector_op = DICOMSeriesSelectorOperator(Sample_Rules_Text)
56+
series_to_vol_op = DICOMSeriesToVolumeOperator()
57+
58+
# Create the inference operator that supports MONAI Bundle and automates the inference.
59+
# The IOMapping labels match the input and prediction keys in the pre and post processing.
60+
# The model_name is optional when the app has only one model.
61+
# The bundle_path argument optionally can be set to an accessible bundle file path in the dev
62+
# environment, so when the app is packaged into a MAP, the operator can complete the bundle parsing
63+
# during init to provide the optional packages info, parsed from the bundle, to the packager
64+
# for it to install the packages in the MAP docker image.
65+
# Setting output IOType to DISK only works only for leaf operators, not the case in this example.
66+
#
67+
# Pertinent MONAI Bundle:
68+
# https://github.com/Project-MONAI/model-zoo/tree/dev/models/spleen_ct_segmentation
69+
70+
bundle_spleen_seg_op = MonaiBundleInferenceOperator(
71+
input_mapping=[IOMapping("image", Image, IOType.IN_MEMORY)],
72+
output_mapping=[IOMapping("pred", Image, IOType.IN_MEMORY)],
73+
bundle_config_names=BundleConfigNames(config_names=["inference"]), # Same as the default
74+
)
75+
76+
# Create DICOM Seg writer providing the required segment description for each segment with
77+
# the actual algorithm and the pertinent organ/tissue. The segment_label, algorithm_name,
78+
# and algorithm_version are of DICOM VR LO type, limited to 64 chars.
79+
# https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
80+
segment_descriptions = [
81+
SegmentDescription(
82+
segment_label="Pancreas",
83+
segmented_property_category=codes.SCT.Organ,
84+
segmented_property_type=codes.SCT.Pancreas,
85+
algorithm_name="volumetric (3D) segmentation of the pancreas from CT image",
86+
algorithm_family=codes.DCM.ArtificialIntelligence,
87+
algorithm_version="0.3.0",
88+
)
89+
]
90+
91+
custom_tags = {"SeriesDescription": "AI generated Seg for research use only. Not for clinical use."}
92+
93+
dicom_seg_writer = DICOMSegmentationWriterOperator(
94+
segment_descriptions=segment_descriptions, custom_tags=custom_tags
95+
)
96+
97+
# Create the processing pipeline, by specifying the source and destination operators, and
98+
# 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"})
100+
self.add_flow(
101+
series_selector_op, series_to_vol_op, {"study_selected_series_list": "study_selected_series_list"}
102+
)
103+
self.add_flow(series_to_vol_op, bundle_spleen_seg_op, {"image": "image"})
104+
# Note below the dicom_seg_writer requires two inputs, each coming from a source operator.
105+
self.add_flow(
106+
series_selector_op, dicom_seg_writer, {"study_selected_series_list": "study_selected_series_list"}
107+
)
108+
self.add_flow(bundle_spleen_seg_op, dicom_seg_writer, {"pred": "seg_image"})
109+
# Create the surface mesh STL conversion operator and add it to the app execution flow, if needed, by
110+
# 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"})
113+
114+
logging.info(f"End {self.compose.__name__}")
115+
116+
117+
# This is a sample series selection rule in JSON, simply selecting CT series.
118+
# If the study has more than 1 CT series, then all of them will be selected.
119+
# Please see more detail in DICOMSeriesSelectorOperator.
120+
Sample_Rules_Text = """
121+
{
122+
"selections": [
123+
{
124+
"name": "CT Series",
125+
"conditions": {
126+
"StudyDescription": "(.*?)",
127+
"Modality": "(?i)CT",
128+
"SeriesDescription": "(.*?)"
129+
}
130+
}
131+
]
132+
}
133+
"""
134+
135+
if __name__ == "__main__":
136+
# Creates the app and test it standalone. When running is this mode, please note the following:
137+
# -m <model file>, for model file path
138+
# -i <DICOM folder>, for input DICOM CT series folder
139+
# -o <output folder>, for the output folder, default $PWD/output
140+
# e.g.
141+
# monai-deploy exec app.py -i input -m model/model.ts
142+
#
143+
logging.basicConfig(level=logging.DEBUG)
144+
app_instance = AIPancreasSegApp(do_run=True)

0 commit comments

Comments
 (0)