1
- # Copyright 2021 MONAI Consortium
1
+ # Copyright 2021-2023 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
10
10
# limitations under the License.
11
11
12
12
import logging
13
+ from pathlib import Path
13
14
14
15
from numpy import uint8
15
16
16
- import monai .deploy .core as md
17
- from monai .deploy .core import DataPath , ExecutionContext , Image , InputContext , IOType , Operator , OutputContext
17
+ from monai .deploy .core import ConditionType , Fragment , Operator , OperatorSpec
18
18
from monai .deploy .operators .monai_seg_inference_operator import InMemImageReader , MonaiSegInferenceOperator
19
19
from monai .transforms import (
20
20
Activationsd ,
30
30
)
31
31
32
32
33
- @md .input ("image" , Image , IOType .IN_MEMORY )
34
- @md .output ("seg_image" , Image , IOType .IN_MEMORY )
35
- @md .output ("saved_images_folder" , DataPath , IOType .DISK )
36
- @md .env (pip_packages = ["monai>=1.0.0" , "torch>=1.5" , "numpy>=1.21" , "nibabel" ])
33
+ # @md.input("image", Image, IOType.IN_MEMORY)
34
+ # @md.output("seg_image", Image, IOType.IN_MEMORY)
35
+ # @md.output("saved_images_folder", DataPath, IOType.DISK)
36
+ # @md.env(pip_packages=["monai>=1.0.0", "torch>=1.5", "numpy>=1.21", "nibabel"])
37
37
class LiverTumorSegOperator (Operator ):
38
38
"""Performs liver and tumor segmentation using a DL model with an image converted from a DICOM CT series.
39
39
@@ -49,27 +49,48 @@ class LiverTumorSegOperator(Operator):
49
49
Note that the App SDK InMemImageReader, derived from MONAI ImageReader, is passed to LoadImaged.
50
50
This derived reader is needed to parse the in memory image object, and return the expected data structure.
51
51
Loading of the model, and predicting using in-proc PyTorch inference is done by MonaiSegInferenceOperator.
52
+
53
+ Named Input:
54
+ image: Image object.
55
+
56
+ Named Outputs:
57
+ seg_image: Image object of the segmentation object.
58
+ saved_images_folder: Path to the folder with intermediate image output, not requiring a downstream receiver.
52
59
"""
53
60
54
- def __init__ (self ):
61
+ DEFAULT_OUTPUT_FOLDER = Path .cwd () / "saved_images_folder"
62
+
63
+ def __init__ (self , frament : Fragment , * args , model_path : Path , output_folder : Path = None , ** kwargs ):
55
64
self .logger = logging .getLogger ("{}.{}" .format (__name__ , type (self ).__name__ ))
56
- super ().__init__ ()
57
65
self ._input_dataset_key = "image"
58
66
self ._pred_dataset_key = "pred"
59
67
60
- def compute (self , op_input : InputContext , op_output : OutputContext , context : ExecutionContext ):
61
- input_image = op_input .get ("image" )
68
+ self .model_path = model_path
69
+ self .output_folder = output_folder if output_folder else LiverTumorSegOperator .DEFAULT_OUTPUT_FOLDER
70
+ self .output_folder .mkdir (parents = True , exist_ok = True )
71
+ self .fragement = frament # Cache and later pass the Fragment/Application to contained operator(s)
72
+ self .input_name_image = "image"
73
+ self .output_name_seg = "seg_image"
74
+
75
+ self .fragement = frament # Cache and later pass the Fragment/Application to contained operator(s)
76
+ super ().__init__ (frament , * args , ** kwargs )
77
+
78
+ def setup (self , spec : OperatorSpec ):
79
+ spec .input (self .input_name_image )
80
+ spec .output (self .output_name_seg )
81
+
82
+ def compute (self , op_input , op_output , context ):
83
+ input_image = op_input .receive (self .input_name_image )
62
84
if not input_image :
63
85
raise ValueError ("Input image is not found." )
64
86
65
87
# Get the output path from the execution context for saving file(s) to app output.
66
- # Without using this path, operator would be saving files to its designated path, e.g.
67
- # $PWD/.monai_workdir/operators/6048d75a-5de1-45b9-8bd1-2252f88827f2/0/output
68
- op_output_folder_name = DataPath ("saved_images_folder" )
69
- op_output .set (op_output_folder_name , "saved_images_folder" )
70
- op_output_folder_path = op_output .get ("saved_images_folder" ).path
71
- op_output_folder_path .mkdir (parents = True , exist_ok = True )
72
- print (f"Operator output folder path: { op_output_folder_path } " )
88
+ # Without using this path, operator would be saving files to its designated path
89
+ # op_output_folder_name = "saved_images_folder"
90
+ # op_output.set(op_output_folder_name, "saved_images_folder")
91
+ # op_output_folder_path = Path(op_output_folder_name) # op_output.get("saved_images_folder").path
92
+ # op_output_folder_path.mkdir(parents=True, exist_ok=True)
93
+ # print(f"Operator output folder path: {op_output_folder_path}")
73
94
74
95
# This operator gets an in-memory Image object, so a specialized ImageReader is needed.
75
96
_reader = InMemImageReader (input_image )
@@ -78,28 +99,30 @@ def compute(self, op_input: InputContext, op_output: OutputContext, context: Exe
78
99
# They are both saved in the same subfolder of the application output folder, with names
79
100
# distinguished by postfix. They can also be save in different subfolder if need be.
80
101
# These images files can then be packaged for rendering.
81
- pre_transforms = self .pre_process (_reader , op_output_folder_path )
82
- post_transforms = self .post_process (pre_transforms , op_output_folder_path )
102
+ pre_transforms = self .pre_process (_reader , str ( self . output_folder ) )
103
+ post_transforms = self .post_process (pre_transforms , str ( self . output_folder ) )
83
104
84
105
# Delegates inference and saving output to the built-in operator.
85
106
infer_operator = MonaiSegInferenceOperator (
86
- (
107
+ self .fragement ,
108
+ roi_size = (
87
109
160 ,
88
110
160 ,
89
111
160 ,
90
112
),
91
- pre_transforms ,
92
- post_transforms ,
113
+ pre_transforms = pre_transforms ,
114
+ post_transforms = post_transforms ,
93
115
overlap = 0.6 ,
94
116
model_name = "" ,
117
+ model_path = self .model_path ,
95
118
)
96
119
97
120
# Setting the keys used in the dictironary based transforms may change.
98
121
infer_operator .input_dataset_key = self ._input_dataset_key
99
122
infer_operator .pred_dataset_key = self ._pred_dataset_key
100
123
101
- # Now let the built-in operator handles the work with the I/O spec and execution context.
102
- infer_operator .compute ( op_input , op_output , context )
124
+ # Now let the built-in operator handle the work with the I/O spec and execution context.
125
+ op_output . emit ( infer_operator .compute_impl ( input_image , context ), self . output_name_seg )
103
126
104
127
def pre_process (self , img_reader , out_dir : str = "./input_images" ) -> Compose :
105
128
"""Composes transforms for preprocessing input before predicting on a model."""
0 commit comments