Skip to content

Commit 3bf72b7

Browse files
slbrysonwyli
andauthored
336 Add MedNIST Classification example to Triton Demo (Project-MONAI#337)
* intermediate changes for MedNIST Signed-off-by: Sidney <[email protected]> * first pass for classification Signed-off-by: Sidney Bryson <[email protected]> * MONAI MedNIST * MONAI MedNIST Signed-off-by: Sidney Bryson <[email protected]> * Mednist Updates Signed-off-by: Sidney Bryson <[email protected]> * Add MedNIST Ex. (Project-MONAI#336) Signed-off-by: Sidney Bryson <[email protected]> * Add MedNIST Ex. (Project-MONAI#336) Signed-off-by: Sidney Bryson <[email protected]> * Add MedNIST Ex. (Project-MONAI#336) Signed-off-by: Sidney Bryson <[email protected]> Co-authored-by: Wenqi Li <[email protected]>
1 parent b7bf1d0 commit 3bf72b7

File tree

13 files changed

+491
-21
lines changed

13 files changed

+491
-21
lines changed

deployment/Triton/.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
__pycache__
33
# Ignore Pipenv files
44
Pipfile
5-
Pipfile.loc
5+
Pipfile.lock
6+
# Image Input Files
7+
*.jpeg
8+
*.gz

deployment/Triton/Dockerfile

+9-4
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,22 @@ FROM nvcr.io/nvidia/tritonserver:21.04-py3
4141

4242
# create model directory in container
4343
RUN mkdir -p /models/monai_covid/1
44-
RUN mkdir -p /models/monai_covid/
45-
44+
RUN mkdir -p /models/mednist_class/1
4645
# install project-specific dependencies
4746
COPY requirements.txt .
47+
4848
RUN pip install -r requirements.txt
4949
RUN rm requirements.txt
5050

5151
# copy contents of model project into model repo in container image
5252
COPY models/monai_covid/config.pbtxt /models/monai_covid
5353
COPY models/monai_covid/1/model.py /models/monai_covid/1
54-
#COPY models/monai_covid/1/covid19_model.ts /models/monai_covid/1 #moved to google drive
54+
55+
56+
# copy contents of model project into model repo in container image
57+
COPY models/mednist_class/config.pbtxt /models/mednist_class
58+
COPY models/mednist_class/1/model.py /models/mednist_class/1
59+
5560

5661
ENTRYPOINT [ "tritonserver", "--model-repository=/models"]
57-
#ENTRYPOINT [ "tritonserver", "--model-repository=/models", "--log-verbose=1"]
62+
# ENTRYPOINT [ "tritonserver", "--model-repository=/models", "--log-verbose=1"]

deployment/Triton/README.md

+19-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ $ ./triton_build.sh
8383
2. Run Triton Container Image in Background Terminal using provided shell script
8484
The supplied script will start the demo container with Triton and expose the three ports to localhost needed for the application to send inference requests.
8585
```
86-
$ ./triton_run_local.sh
86+
$ ./run_triton_local.sh
8787
```
8888
3. Install environment for client
8989
The client environment should have Python 3 installed and should have the necessary packages installed.
@@ -171,7 +171,7 @@ filename = 'client/test_data/volume-covid19-A-0000.nii.gz'
171171
```
172172
- The client calls the Triton Service using the external port configured previously.
173173
```python:
174-
with httpclient.InferenceServerClient("localhost:8000") as client:
174+
with httpclient.InferenceServerClient("localhost:7555") as client:
175175
```
176176
- The Triton inference response is returned :
177177
```python:
@@ -182,6 +182,23 @@ response = client.infer(model_name,
182182
183183
result = response.get_response()
184184
```
185+
-------
186+
## MedNIST Classification Example
187+
188+
- Added to this demo as alternate demo using the MedNIST dataset in a classification example.
189+
- To run the MedNIST example use the same steps as shown in the [Quick Start](#quick-start) with the following changes at step 5.
190+
5. Run the client program (for the MedNIST example)
191+
The [client](./client/client_mednist.py) program will take an optional file input and perform classification on body parts using the MedNIST data set. A small subset of the database is included.
192+
```
193+
$ mkdir -p client/test_data/MedNist
194+
$ python -u client/client_mednist.py client/test_data/MedNist
195+
```
196+
Alternatively, the user can just run the shell script provided the previous steps 1 -4 in the [Quick Start](#quick-start) were followed.
197+
```
198+
$ ./mednist_client_run.sh
199+
```
200+
The expected result is variety of classification results for body images and local inference times.
201+
185202
-------
186203
## Usage
187204
[See Triton Inference Server/python_backend documentation](https://github.com/triton-inference-server/python_backend#usage)

deployment/Triton/client/client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def open_nifti_files(input_path):
8585
print("No valid inputs provided")
8686
sys.exit(1)
8787

88-
with httpclient.InferenceServerClient("localhost:8000") as client:
88+
with httpclient.InferenceServerClient("localhost:7555") as client:
8989
image_bytes = b''
9090
for nifti_file in nifti_files:
9191
with open(nifti_file, 'rb') as f:
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright 2020 - 2021 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+
13+
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
14+
#
15+
# Redistribution and use in source and binary forms, with or without
16+
# modification, are permitted provided that the following conditions
17+
# are met:
18+
# * Redistributions of source code must retain the above copyright
19+
# notice, this list of conditions and the following disclaimer.
20+
# * Redistributions in binary form must reproduce the above copyright
21+
# notice, this list of conditions and the following disclaimer in the
22+
# documentation and/or other materials provided with the distribution.
23+
# * Neither the name of NVIDIA CORPORATION nor the names of its
24+
# contributors may be used to endorse or promote products derived
25+
# from this software without specific prior written permission.
26+
#
27+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
28+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
31+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
35+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38+
39+
from tritonclient.utils import *
40+
import tritonclient.grpc as grpcclient
41+
import tritonclient.http as httpclient
42+
43+
import argparse
44+
import numpy as np
45+
import os
46+
import sys
47+
import time
48+
from uuid import uuid4
49+
import glob
50+
51+
from monai.apps.utils import download_and_extract
52+
from monai.transforms.utils import convert_to_numpy
53+
54+
MEDNIST_CLASSES = ["AbdomenCT", "BreastMRI", "CXR", "ChestCT", "Hand", "HeadCT"]
55+
56+
57+
58+
model_name = "mednist_class"
59+
gdrive_path = "https://drive.google.com/uc?id=1HQk4i4vXKUX_aAYR4wcZQKd-qk5Lcm_W"
60+
mednist_filename = "MedNIST_demo.tar.gz"
61+
md5_check = "3f24a5833bb0455a7815c4e0ecc8a810"
62+
63+
64+
def open_jpeg_files(input_path):
65+
return sorted(glob.glob(os.path.join(input_path, "*.jpeg")))
66+
67+
68+
if __name__ == "__main__":
69+
70+
parser = argparse.ArgumentParser(description='Triton CLI for MedNist classification inference from JPEG data')
71+
parser.add_argument(
72+
'input',
73+
type=str,
74+
help="Path to JPEG file or directory containing JPEG files to send for MedNist classification"
75+
)
76+
args = parser.parse_args()
77+
78+
jpeg_files = []
79+
extract_dir = "./client/test_data/MedNist"
80+
tar_save_path = os.path.join(extract_dir, mednist_filename)
81+
if os.path.isdir(args.input): # check for directory existence
82+
# Grab files from Google Drive and place in directory
83+
download_and_extract(gdrive_path, tar_save_path, output_dir=extract_dir, hash_val=md5_check, hash_type="md5")
84+
jpeg_files = open_jpeg_files(args.input)
85+
86+
elif os.path.isfile(args.input):
87+
jpeg_files = [args.input]
88+
89+
if not jpeg_files:
90+
print("No valid inputs provided")
91+
sys.exit(1)
92+
93+
with httpclient.InferenceServerClient("localhost:7555") as client:
94+
image_bytes = b''
95+
for jpeg_file in jpeg_files:
96+
with open(jpeg_file, 'rb') as f:
97+
image_bytes = f.read()
98+
99+
input0_data = np.array([[image_bytes]], dtype=np.bytes_)
100+
101+
inputs = [
102+
httpclient.InferInput("INPUT0", input0_data.shape, np_to_triton_dtype(input0_data.dtype)),
103+
]
104+
105+
inputs[0].set_data_from_numpy(input0_data)
106+
107+
outputs = [
108+
httpclient.InferRequestedOutput("OUTPUT0"),
109+
]
110+
111+
inference_start_time = time.time() * 1000
112+
response = client.infer(model_name,
113+
inputs,
114+
request_id=str(uuid4().hex),
115+
outputs=outputs,)
116+
inference_time = time.time() * 1000 - inference_start_time
117+
118+
result = response.get_response()
119+
print("Classification result for `{}`: {}. (Inference time: {:6.0f} ms)".format(
120+
jpeg_file,
121+
response.as_numpy("OUTPUT0").astype(str)[0],
122+
inference_time,
123+
))

deployment/Triton/client_run.sh

+10-2
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,13 @@
3636
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3737
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3838
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39-
40-
python -u client/client.py client/test_data
39+
# if no argument provided use default `client/test_data` name
40+
if [ $# -eq 0 ]
41+
then
42+
echo "No arguments supplied defaulting 'client/test_data'"
43+
input_name="client/test_data"
44+
else
45+
# otherwise use the first argument as the operator image
46+
input_name=$1
47+
fi
48+
python -u client/client.py $input_name
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/bash
2+
# Copyright 2020 - 2021 MONAI Consortium
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
14+
# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
15+
#
16+
# Redistribution and use in source and binary forms, with or without
17+
# modification, are permitted provided that the following conditions
18+
# are met:
19+
# * Redistributions of source code must retain the above copyright
20+
# notice, this list of conditions and the following disclaimer.
21+
# * Redistributions in binary form must reproduce the above copyright
22+
# notice, this list of conditions and the following disclaimer in the
23+
# documentation and/or other materials provided with the distribution.
24+
# * Neither the name of NVIDIA CORPORATION nor the names of its
25+
# contributors may be used to endorse or promote products derived
26+
# from this software without specific prior written permission.
27+
#
28+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
29+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
32+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
36+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39+
# if no argument provided use default `client/test_data` name
40+
if [ $# -eq 0 ]
41+
then
42+
echo "No arguments supplied defaulting './client/test_data/MedNist/demo'"
43+
input_name="./client/test_data/MedNist"
44+
# check if the output directory exists
45+
if [ -d $input_name ]
46+
then
47+
echo "Directory $PWD/client/test_data/MedNist exists."
48+
else
49+
echo "Error: Directory $PWD/client/test_data/MedNist does not exist.
50+
Please create the directory to save data to disk from the Clara operator."
51+
exit 1
52+
fi
53+
else
54+
# otherwise use the first argument as the operator image
55+
input_name=$1
56+
fi
57+
python -u client/client_mednist.py $input_name
58+

0 commit comments

Comments
 (0)