Creating a Deploy App with MONAI Deploy App SDK and MONAI Bundle

This tutorial shows how to create an organ segmentation application for a PyTorch model that has been trained with MONAI and packaged in the MONAI Bundle format.

Deploying AI models requires the integration with clinical imaging network, even if in a for-research-use setting. This means that the AI deploy application will need to support standards-based imaging protocols, and specifically for Radiological imaging, DICOM protocol.

Typically, DICOM network communication, either in DICOM TCP/IP network protocol or DICOMWeb, would be handled by DICOM devices or services, e.g. MONAI Deploy Informatics Gateway, so the deploy application itself would only need to use DICOM Part 10 files as input and save the AI result in DICOM Part10 file(s). For segmentation use cases, the DICOM instance file for AI results could be a DICOM Segmentation object or a DICOM RT Structure Set, and for classification, DICOM Structure Report and/or DICOM Encapsulated PDF.

When integrated with imaging networks and receiving DICOM instances from modalities and Picture Archiving and Communications System (PACS), an AI deploy application has to deal with a whole DICOM study with multiple series, whose images’ spacing may not be the same as expected by the trained model. To address these cases consistently and efficiently, MONAI Deploy Application SDK provides classes, called operators, to parse DICOM studies, select specific series with application-defined rules, and convert the selected DICOM series into domain-specific image format along with meta-data representing the pertinent DICOM attributes. The image is then further processed in the pre-processing stage to normalize spacing, orientation, intensity,etc, before pixel data as Tensors are used for inference.

In the following sections, we will demonstrate how to create a MONAI Deploy application package using the MONAI Deploy App SDK, and importantly, using the built-in MONAI Bundle Inference Operator to perform inference with the Spleen CT Segmentation PyTorch model in a MONAI Bundle.

Note

For local testing, if there is a lack of DICOM Part 10 files, one can use open source programs, e.g. 3D Slicer, to convert a NIfTI file to a DICOM series.

To make running this example simpler, the DICOM files and the Spleen CT Segmentation MONAI Bundle, published in MONAI Model Zoo, have been packaged and shared on Google Drive.

Creating Operators and connecting them in Application class

We will implement an application that consists of five Operators:

  • DICOMDataLoaderOperator:

    • Input(dicom_files): a folder path (DataPath)

    • Output(dicom_study_list): a list of DICOM studies in memory (List[DICOMStudy])

  • DICOMSeriesSelectorOperator:

    • Input(dicom_study_list): a list of DICOM studies in memory (List[DICOMStudy])

    • Input(selection_rules): a selection rule (Dict)

    • Output(study_selected_series_list): a DICOM series object in memory (StudySelectedSeries)

  • DICOMSeriesToVolumeOperator:

    • Input(study_selected_series_list): a DICOM series object in memory (StudySelectedSeries)

    • Output(image): an image object in memory (Image)

  • MonaiBundleInferenceOperator:

    • Input(image): an image object in memory (Image)

    • Output(pred): an image object in memory (Image)

  • DICOMSegmentationWriterOperator:

    • Input(seg_image): a segmentation image object in memory (Image)

    • Input(study_selected_series_list): a DICOM series object in memory (StudySelectedSeries)

    • Output(dicom_seg_instance): a file path (DataPath)

Note

The DICOMSegmentationWriterOperator needs both the segmentation image as well as the original DICOM series meta-data in order to use the patient demographics and the DICOM Study level attributes.

The workflow of the application is illustrated below.

%%{init: {"theme": "base", "themeVariables": { "fontSize": "16px"}} }%% classDiagram direction TB DICOMDataLoaderOperator --|> DICOMSeriesSelectorOperator : dicom_study_list...dicom_study_list DICOMSeriesSelectorOperator --|> DICOMSeriesToVolumeOperator : study_selected_series_list...study_selected_series_list DICOMSeriesToVolumeOperator --|> MonaiBundleInferenceOperator : image...image DICOMSeriesSelectorOperator --|> DICOMSegmentationWriterOperator : study_selected_series_list...study_selected_series_list MonaiBundleInferenceOperator --|> DICOMSegmentationWriterOperator : pred...seg_image class DICOMDataLoaderOperator { <in>dicom_files : DISK dicom_study_list(out) IN_MEMORY } class DICOMSeriesSelectorOperator { <in>dicom_study_list : IN_MEMORY <in>selection_rules : IN_MEMORY study_selected_series_list(out) IN_MEMORY } class DICOMSeriesToVolumeOperator { <in>study_selected_series_list : IN_MEMORY image(out) IN_MEMORY } class MonaiBundleInferenceOperator { <in>image : IN_MEMORY pred(out) IN_MEMORY } class DICOMSegmentationWriterOperator { <in>seg_image : IN_MEMORY <in>study_selected_series_list : IN_MEMORY dicom_seg_instance(out) DISK }

Setup environment

# Install MONAI and other necessary image processing packages for the application
!python -c "import monai" || pip install --upgrade -q "monai"
!python -c "import torch" || pip install -q "torch>=1.10.2"
!python -c "import numpy" || pip install -q "numpy>=1.21"
!python -c "import nibabel" || pip install -q "nibabel>=3.2.1"
!python -c "import pydicom" || pip install -q "pydicom>=1.4.2"
!python -c "import highdicom" || pip install -q "highdicom>=0.18.2"
!python -c "import SimpleITK" || pip install -q "SimpleITK>=2.0.0"
!python -c "import typeguard" || pip install -q "typeguard>=2.12.1"

# Install MONAI Deploy App SDK package
!python -c "import monai.deploy" || pip install --upgrade -q "monai-deploy-app-sdk"

Note: you may need to restart the Jupyter kernel to use the updated packages.

Download/Extract input and model/bundle files from Google Drive

# Download the test data and MONAI bundle zip file
!pip install gdown 
!gdown "https://drive.google.com/uc?id=1Uds8mEvdGNYUuvFpTtCQ8gNU97bAPCaQ"

# After downloading ai_spleen_bundle_data zip file from the web browser or using gdown,
!unzip -o "ai_spleen_seg_bundle_data.zip"
Requirement already satisfied: gdown in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (4.5.1)
Requirement already satisfied: filelock in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (3.8.0)
Requirement already satisfied: beautifulsoup4 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (4.11.1)
Requirement already satisfied: tqdm in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (4.64.0)
Requirement already satisfied: requests[socks] in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (2.28.1)
Requirement already satisfied: six in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (1.16.0)
Requirement already satisfied: soupsieve>1.2 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from beautifulsoup4->gdown) (2.3.2.post1)
Requirement already satisfied: idna<4,>=2.5 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from requests[socks]->gdown) (3.3)
Requirement already satisfied: certifi>=2017.4.17 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from requests[socks]->gdown) (2022.6.15)
Requirement already satisfied: charset-normalizer<3,>=2 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from requests[socks]->gdown) (2.1.1)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from requests[socks]->gdown) (1.26.12)
Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from requests[socks]->gdown) (1.7.1)
Downloading...
From: https://drive.google.com/uc?id=1Uds8mEvdGNYUuvFpTtCQ8gNU97bAPCaQ
To: /home/mqin/src/monai-deploy-app-sdk/notebooks/tutorials/ai_spleen_seg_bundle_data.zip
100%|██████████████████████████████████████| 79.4M/79.4M [00:00<00:00, 97.8MB/s]
Archive:  ai_spleen_seg_bundle_data.zip
  inflating: dcm/1-001.dcm           
  inflating: dcm/1-002.dcm           
  inflating: dcm/1-003.dcm           
  inflating: dcm/1-004.dcm           
  inflating: dcm/1-005.dcm           
  inflating: dcm/1-006.dcm           
  inflating: dcm/1-007.dcm           
  inflating: dcm/1-008.dcm           
  inflating: dcm/1-009.dcm           
  inflating: dcm/1-010.dcm           
  inflating: dcm/1-011.dcm           
  inflating: dcm/1-012.dcm           
  inflating: dcm/1-013.dcm           
  inflating: dcm/1-014.dcm           
  inflating: dcm/1-015.dcm           
  inflating: dcm/1-016.dcm           
  inflating: dcm/1-017.dcm           
  inflating: dcm/1-018.dcm           
  inflating: dcm/1-019.dcm           
  inflating: dcm/1-020.dcm           
  inflating: dcm/1-021.dcm           
  inflating: dcm/1-022.dcm           
  inflating: dcm/1-023.dcm           
  inflating: dcm/1-024.dcm           
  inflating: dcm/1-025.dcm           
  inflating: dcm/1-026.dcm           
  inflating: dcm/1-027.dcm           
  inflating: dcm/1-028.dcm           
  inflating: dcm/1-029.dcm           
  inflating: dcm/1-030.dcm           
  inflating: dcm/1-031.dcm           
  inflating: dcm/1-032.dcm           
  inflating: dcm/1-033.dcm           
  inflating: dcm/1-034.dcm           
  inflating: dcm/1-035.dcm           
  inflating: dcm/1-036.dcm           
  inflating: dcm/1-037.dcm           
  inflating: dcm/1-038.dcm           
  inflating: dcm/1-039.dcm           
  inflating: dcm/1-040.dcm           
  inflating: dcm/1-041.dcm           
  inflating: dcm/1-042.dcm           
  inflating: dcm/1-043.dcm           
  inflating: dcm/1-044.dcm           
  inflating: dcm/1-045.dcm           
  inflating: dcm/1-046.dcm           
  inflating: dcm/1-047.dcm           
  inflating: dcm/1-048.dcm           
  inflating: dcm/1-049.dcm           
  inflating: dcm/1-050.dcm           
  inflating: dcm/1-051.dcm           
  inflating: dcm/1-052.dcm           
  inflating: dcm/1-053.dcm           
  inflating: dcm/1-054.dcm           
  inflating: dcm/1-055.dcm           
  inflating: dcm/1-056.dcm           
  inflating: dcm/1-057.dcm           
  inflating: dcm/1-058.dcm           
  inflating: dcm/1-059.dcm           
  inflating: dcm/1-060.dcm           
  inflating: dcm/1-061.dcm           
  inflating: dcm/1-062.dcm           
  inflating: dcm/1-063.dcm           
  inflating: dcm/1-064.dcm           
  inflating: dcm/1-065.dcm           
  inflating: dcm/1-066.dcm           
  inflating: dcm/1-067.dcm           
  inflating: dcm/1-068.dcm           
  inflating: dcm/1-069.dcm           
  inflating: dcm/1-070.dcm           
  inflating: dcm/1-071.dcm           
  inflating: dcm/1-072.dcm           
  inflating: dcm/1-073.dcm           
  inflating: dcm/1-074.dcm           
  inflating: dcm/1-075.dcm           
  inflating: dcm/1-076.dcm           
  inflating: dcm/1-077.dcm           
  inflating: dcm/1-078.dcm           
  inflating: dcm/1-079.dcm           
  inflating: dcm/1-080.dcm           
  inflating: dcm/1-081.dcm           
  inflating: dcm/1-082.dcm           
  inflating: dcm/1-083.dcm           
  inflating: dcm/1-084.dcm           
  inflating: dcm/1-085.dcm           
  inflating: dcm/1-086.dcm           
  inflating: dcm/1-087.dcm           
  inflating: dcm/1-088.dcm           
  inflating: dcm/1-089.dcm           
  inflating: dcm/1-090.dcm           
  inflating: dcm/1-091.dcm           
  inflating: dcm/1-092.dcm           
  inflating: dcm/1-093.dcm           
  inflating: dcm/1-094.dcm           
  inflating: dcm/1-095.dcm           
  inflating: dcm/1-096.dcm           
  inflating: dcm/1-097.dcm           
  inflating: dcm/1-098.dcm           
  inflating: dcm/1-099.dcm           
  inflating: dcm/1-100.dcm           
  inflating: dcm/1-101.dcm           
  inflating: dcm/1-102.dcm           
  inflating: dcm/1-103.dcm           
  inflating: dcm/1-104.dcm           
  inflating: dcm/1-105.dcm           
  inflating: dcm/1-106.dcm           
  inflating: dcm/1-107.dcm           
  inflating: dcm/1-108.dcm           
  inflating: dcm/1-109.dcm           
  inflating: dcm/1-110.dcm           
  inflating: dcm/1-111.dcm           
  inflating: dcm/1-112.dcm           
  inflating: dcm/1-113.dcm           
  inflating: dcm/1-114.dcm           
  inflating: dcm/1-115.dcm           
  inflating: dcm/1-116.dcm           
  inflating: dcm/1-117.dcm           
  inflating: dcm/1-118.dcm           
  inflating: dcm/1-119.dcm           
  inflating: dcm/1-120.dcm           
  inflating: dcm/1-121.dcm           
  inflating: dcm/1-122.dcm           
  inflating: dcm/1-123.dcm           
  inflating: dcm/1-124.dcm           
  inflating: dcm/1-125.dcm           
  inflating: dcm/1-126.dcm           
  inflating: dcm/1-127.dcm           
  inflating: dcm/1-128.dcm           
  inflating: dcm/1-129.dcm           
  inflating: dcm/1-130.dcm           
  inflating: dcm/1-131.dcm           
  inflating: dcm/1-132.dcm           
  inflating: dcm/1-133.dcm           
  inflating: dcm/1-134.dcm           
  inflating: dcm/1-135.dcm           
  inflating: dcm/1-136.dcm           
  inflating: dcm/1-137.dcm           
  inflating: dcm/1-138.dcm           
  inflating: dcm/1-139.dcm           
  inflating: dcm/1-140.dcm           
  inflating: dcm/1-141.dcm           
  inflating: dcm/1-142.dcm           
  inflating: dcm/1-143.dcm           
  inflating: dcm/1-144.dcm           
  inflating: dcm/1-145.dcm           
  inflating: dcm/1-146.dcm           
  inflating: dcm/1-147.dcm           
  inflating: dcm/1-148.dcm           
  inflating: dcm/1-149.dcm           
  inflating: dcm/1-150.dcm           
  inflating: dcm/1-151.dcm           
  inflating: dcm/1-152.dcm           
  inflating: dcm/1-153.dcm           
  inflating: dcm/1-154.dcm           
  inflating: dcm/1-155.dcm           
  inflating: dcm/1-156.dcm           
  inflating: dcm/1-157.dcm           
  inflating: dcm/1-158.dcm           
  inflating: dcm/1-159.dcm           
  inflating: dcm/1-160.dcm           
  inflating: dcm/1-161.dcm           
  inflating: dcm/1-162.dcm           
  inflating: dcm/1-163.dcm           
  inflating: dcm/1-164.dcm           
  inflating: dcm/1-165.dcm           
  inflating: dcm/1-166.dcm           
  inflating: dcm/1-167.dcm           
  inflating: dcm/1-168.dcm           
  inflating: dcm/1-169.dcm           
  inflating: dcm/1-170.dcm           
  inflating: dcm/1-171.dcm           
  inflating: dcm/1-172.dcm           
  inflating: dcm/1-173.dcm           
  inflating: dcm/1-174.dcm           
  inflating: dcm/1-175.dcm           
  inflating: dcm/1-176.dcm           
  inflating: dcm/1-177.dcm           
  inflating: dcm/1-178.dcm           
  inflating: dcm/1-179.dcm           
  inflating: dcm/1-180.dcm           
  inflating: dcm/1-181.dcm           
  inflating: dcm/1-182.dcm           
  inflating: dcm/1-183.dcm           
  inflating: dcm/1-184.dcm           
  inflating: dcm/1-185.dcm           
  inflating: dcm/1-186.dcm           
  inflating: dcm/1-187.dcm           
  inflating: dcm/1-188.dcm           
  inflating: dcm/1-189.dcm           
  inflating: dcm/1-190.dcm           
  inflating: dcm/1-191.dcm           
  inflating: dcm/1-192.dcm           
  inflating: dcm/1-193.dcm           
  inflating: dcm/1-194.dcm           
  inflating: dcm/1-195.dcm           
  inflating: dcm/1-196.dcm           
  inflating: dcm/1-197.dcm           
  inflating: dcm/1-198.dcm           
  inflating: dcm/1-199.dcm           
  inflating: dcm/1-200.dcm           
  inflating: dcm/1-201.dcm           
  inflating: dcm/1-202.dcm           
  inflating: dcm/1-203.dcm           
  inflating: dcm/1-204.dcm           
  inflating: model.ts                

Setup imports

Let’s import necessary classes/decorators to define Application and Operator.

import logging

# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.
from pydicom.sr.codedict import codes

from monai.deploy.core import Application, resource
from monai.deploy.core.domain import Image
from monai.deploy.core.io_type import IOType
from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator
from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator, SegmentDescription
from monai.deploy.operators.dicom_series_selector_operator import DICOMSeriesSelectorOperator
from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator
from monai.deploy.operators.monai_bundle_inference_operator import IOMapping, MonaiBundleInferenceOperator

Determining the Input and Output for the Model Bundle Inference Operator

The App SDK provides a MonaiBundleInferenceOperator class to perform inference with a MONAI Bundle, which is essentially a PyTorch model in TorchScript with additional metadata describing the model network and processing specification. This operator uses the MONAI utilities to parse a MONAI Bundle to automatically instantiate the objects required for input and output processing as well as inference, as such it depends on MONAI transforms, inferers, and in turn their dependencies.

Each Operator class inherits from the base Operator class, and its input/output properties are specified by using @input/@output decorators. For the MonaiBundleInferenceOperator class, the input/output need to be defined to match those of the model network, both in name and data type. For the current release, an IOMapping object is used to connect the operator input/output to those of the model network by using the same names. This is likely to change, to be automated, in the future releases once certain limitation in the App SDK is removed.

The Spleen CT Segmentation model network has a named input, called “image”, and the named output called “pred”, and both are of image type, which can all be mapped to the App SDK Image. This piece of information is typically acquired by examining the model metadata network_data_format attribute in the bundle, as seen in this [example] (https://github.com/Project-MONAI/model-zoo/blob/dev/models/spleen_ct_segmentation/configs/metadata.json).

Creating Application class

Our application class would look like below.

It defines App class, inheriting Application class.

The requirements (resource and package dependency) for the App can be specified by using @resource and @env decorators.

Objects required for DICOM parsing, series selection, pixel data conversion to volume image, model specific inference, and the AI result specific DICOM Segmentation object writers are created. The execution pipeline, as a Directed Acyclic Graph, is then constructed by connecting these objects through self.add_flow().

@resource(cpu=1, gpu=1, memory="7Gi")
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
# The monai pkg is not required by this class, instead by the included operators.
class AISpleenSegApp(Application):
    def __init__(self, *args, **kwargs):
        """Creates an application instance."""
        self._logger = logging.getLogger("{}.{}".format(__name__, type(self).__name__))
        super().__init__(*args, **kwargs)

    def run(self, *args, **kwargs):
        # This method calls the base class to run. Can be omitted if simply calling through.
        self._logger.info(f"Begin {self.run.__name__}")
        super().run(*args, **kwargs)
        self._logger.info(f"End {self.run.__name__}")

    def compose(self):
        """Creates the app specific operators and chain them up in the processing DAG."""

        logging.info(f"Begin {self.compose.__name__}")

        # Create the custom operator(s) as well as SDK built-in operator(s).
        study_loader_op = DICOMDataLoaderOperator()
        series_selector_op = DICOMSeriesSelectorOperator()
        series_to_vol_op = DICOMSeriesToVolumeOperator()

        # Create the inference operator that supports MONAI Bundle and automates the inference.
        # The IOMapping labels match the input and prediction keys in the pre and post processing.
        # The model_name is optional when the app has only one model.
        # The bundle_path argument optionally can be set to an accessible bundle file path in the dev
        # environment, so when the app is packaged into a MAP, the operator can complete the bundle parsing
        # during init to provide the optional packages info, parsed from the bundle, to the packager
        # for it to install the packages in the MAP docker image.
        # Setting output IOType to DISK only works only for leaf operators, not the case in this example.
        #
        # Pertinent MONAI Bundle:
        #   https://github.com/Project-MONAI/model-zoo/tree/dev/models/spleen_ct_segmentation
        bundle_spleen_seg_op = MonaiBundleInferenceOperator(
            input_mapping=[IOMapping("image", Image, IOType.IN_MEMORY)],
            output_mapping=[IOMapping("pred", Image, IOType.IN_MEMORY)],
        )

        # Create DICOM Seg writer providing the required segment description for each segment with
        # the actual algorithm and the pertinent organ/tissue. The segment_label, algorithm_name,
        # and algorithm_version are of DICOM VR LO type, limited to 64 chars.
        # https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
        segment_descriptions = [
            SegmentDescription(
                segment_label="Spleen",
                segmented_property_category=codes.SCT.Organ,
                segmented_property_type=codes.SCT.Spleen,
                algorithm_name="volumetric (3D) segmentation of the spleen from CT image",
                algorithm_family=codes.DCM.ArtificialIntelligence,
                algorithm_version="0.1.0",
            )
        ]
        custom_tags = {"SeriesDescription": "AI generated Seg, not for clinical use."}

        dicom_seg_writer = DICOMSegmentationWriterOperator(
            segment_descriptions=segment_descriptions, custom_tags=custom_tags
        )
        # Create the processing pipeline, by specifying the source and destination operators, and
        # ensuring the output from the former matches the input of the latter, in both name and type.
        self.add_flow(study_loader_op, series_selector_op, {"dicom_study_list": "dicom_study_list"})
        self.add_flow(
            series_selector_op, series_to_vol_op, {"study_selected_series_list": "study_selected_series_list"}
        )
        self.add_flow(series_to_vol_op, bundle_spleen_seg_op, {"image": "image"})
        # Note below the dicom_seg_writer requires two inputs, each coming from a source operator.
        self.add_flow(
            series_selector_op, dicom_seg_writer, {"study_selected_series_list": "study_selected_series_list"}
        )
        self.add_flow(bundle_spleen_seg_op, dicom_seg_writer, {"pred": "seg_image"})
        # Create the surface mesh STL conversion operator and add it to the app execution flow, if needed, by
        # uncommenting the following couple lines.
        # stl_conversion_op = STLConversionOperator(output_file="stl/spleen.stl")
        # self.add_flow(bundle_spleen_seg_op, stl_conversion_op, {"pred": "image"})

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

Executing app locally

We can execute the app in the Jupyter notebook. Note that the DICOM files of the CT Abdomen series must be present in the dcm and the Torch Script model at model.ts. Please use the actual path in your environment.

app = AISpleenSegApp()

app.run(input="dcm", output="output", model="model.ts")
2022-10-18 18:05:37,621 - Begin compose
2022-10-18 18:05:37,623 - End compose
2022-10-18 18:05:37,624 - Begin run
Going to initiate execution of operator DICOMDataLoaderOperator
Executing operator DICOMDataLoaderOperator (Process ID: 1020491, Operator ID: 0e527a77-f91e-46c3-bf14-439de5e86397)
[2022-10-18 18:05:38,538] [WARNING] (root) - No selection rules given; select all series.
[2022-10-18 18:05:38,538] [INFO] (root) - Working on study, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291
[2022-10-18 18:05:38,539] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-18 18:05:38,540] [INFO] (root) - Working on study, instance UID: 1.2.826.0.1.3680043.2.1125.1.67295333199898911264201812221946213
[2022-10-18 18:05:38,540] [INFO] (root) - Working on series, instance UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
Done performing execution of operator DICOMDataLoaderOperator

Going to initiate execution of operator DICOMSeriesSelectorOperator
Executing operator DICOMSeriesSelectorOperator (Process ID: 1020491, Operator ID: 4599586f-54ea-4945-93f7-1abdd960152f)
Working on study, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291
Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
Working on study, instance UID: 1.2.826.0.1.3680043.2.1125.1.67295333199898911264201812221946213
Working on series, instance UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
Done performing execution of operator DICOMSeriesSelectorOperator

Going to initiate execution of operator DICOMSeriesToVolumeOperator
Executing operator DICOMSeriesToVolumeOperator (Process ID: 1020491, Operator ID: 822ca69d-8436-422b-9a88-82a26a3dcdbb)
Done performing execution of operator DICOMSeriesToVolumeOperator

Going to initiate execution of operator MonaiBundleInferenceOperator
Executing operator MonaiBundleInferenceOperator (Process ID: 1020491, Operator ID: 2ea1e94f-69ce-4ece-9af0-716a99565bdc)
Done performing execution of operator MonaiBundleInferenceOperator

Going to initiate execution of operator DICOMSegmentationWriterOperator
Executing operator DICOMSegmentationWriterOperator (Process ID: 1020491, Operator ID: f1a57c69-a36e-4492-b997-56775810c852)
/home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages/highdicom/valuerep.py:54: UserWarning: The string "C3N-00198" is unlikely to represent the intended person name since it contains only a single component. Construct a person name according to the format in described in http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html#sect_6.2.1.2, or, in pydicom 2.2.0 or later, use the pydicom.valuerep.PersonName.from_named_components() method to construct the person name correctly. If a single-component name is really intended, add a trailing caret character to disambiguate the name.
  warnings.warn(
[2022-10-18 18:05:54,228] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1
[2022-10-18 18:05:54,231] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1
[2022-10-18 18:05:54,234] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1
[2022-10-18 18:05:54,236] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1
[2022-10-18 18:05:54,238] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1
[2022-10-18 18:05:54,241] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1
[2022-10-18 18:05:54,243] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1
[2022-10-18 18:05:54,245] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1
[2022-10-18 18:05:54,246] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1
[2022-10-18 18:05:54,248] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1
[2022-10-18 18:05:54,249] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1
[2022-10-18 18:05:54,251] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1
[2022-10-18 18:05:54,252] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1
[2022-10-18 18:05:54,255] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1
[2022-10-18 18:05:54,260] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1
[2022-10-18 18:05:54,262] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1
[2022-10-18 18:05:54,264] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1
[2022-10-18 18:05:54,267] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1
[2022-10-18 18:05:54,270] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1
[2022-10-18 18:05:54,272] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1
[2022-10-18 18:05:54,276] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1
[2022-10-18 18:05:54,279] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1
[2022-10-18 18:05:54,282] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1
[2022-10-18 18:05:54,285] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1
[2022-10-18 18:05:54,288] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1
[2022-10-18 18:05:54,291] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1
[2022-10-18 18:05:54,294] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1
[2022-10-18 18:05:54,297] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1
[2022-10-18 18:05:54,299] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1
[2022-10-18 18:05:54,302] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1
[2022-10-18 18:05:54,304] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1
[2022-10-18 18:05:54,306] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1
[2022-10-18 18:05:54,309] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1
[2022-10-18 18:05:54,311] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1
[2022-10-18 18:05:54,314] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1
[2022-10-18 18:05:54,316] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1
[2022-10-18 18:05:54,318] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1
[2022-10-18 18:05:54,323] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1
[2022-10-18 18:05:54,326] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1
[2022-10-18 18:05:54,329] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1
[2022-10-18 18:05:54,332] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1
[2022-10-18 18:05:54,335] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1
[2022-10-18 18:05:54,338] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1
[2022-10-18 18:05:54,340] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1
[2022-10-18 18:05:54,343] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1
[2022-10-18 18:05:54,346] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1
[2022-10-18 18:05:54,348] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1
[2022-10-18 18:05:54,351] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1
[2022-10-18 18:05:54,354] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1
[2022-10-18 18:05:54,356] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1
[2022-10-18 18:05:54,359] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1
[2022-10-18 18:05:54,362] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1
[2022-10-18 18:05:54,365] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1
[2022-10-18 18:05:54,368] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1
[2022-10-18 18:05:54,373] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1
[2022-10-18 18:05:54,378] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1
[2022-10-18 18:05:54,382] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1
[2022-10-18 18:05:54,385] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1
[2022-10-18 18:05:54,388] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1
[2022-10-18 18:05:54,391] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1
[2022-10-18 18:05:54,393] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1
[2022-10-18 18:05:54,396] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1
[2022-10-18 18:05:54,399] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1
[2022-10-18 18:05:54,402] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1
[2022-10-18 18:05:54,405] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1
[2022-10-18 18:05:54,407] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1
[2022-10-18 18:05:54,408] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1
[2022-10-18 18:05:54,410] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1
[2022-10-18 18:05:54,411] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1
[2022-10-18 18:05:54,413] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1
[2022-10-18 18:05:54,414] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1
[2022-10-18 18:05:54,416] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1
[2022-10-18 18:05:54,419] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1
[2022-10-18 18:05:54,421] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1
[2022-10-18 18:05:54,423] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1
[2022-10-18 18:05:54,425] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1
[2022-10-18 18:05:54,427] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1
[2022-10-18 18:05:54,429] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1
[2022-10-18 18:05:54,431] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1
[2022-10-18 18:05:54,434] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1
[2022-10-18 18:05:54,436] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1
[2022-10-18 18:05:54,439] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1
[2022-10-18 18:05:54,441] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1
[2022-10-18 18:05:54,443] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1
[2022-10-18 18:05:54,446] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1
[2022-10-18 18:05:54,448] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1
[2022-10-18 18:05:54,451] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1
[2022-10-18 18:05:54,494] [INFO] (highdicom.base) - copy Image-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:05:54,495] [INFO] (highdicom.base) - copy attributes of module "Specimen"
[2022-10-18 18:05:54,496] [INFO] (highdicom.base) - copy Patient-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:05:54,497] [INFO] (highdicom.base) - copy attributes of module "Patient"
[2022-10-18 18:05:54,498] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Subject"
[2022-10-18 18:05:54,499] [INFO] (highdicom.base) - copy Study-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:05:54,499] [INFO] (highdicom.base) - copy attributes of module "General Study"
[2022-10-18 18:05:54,500] [INFO] (highdicom.base) - copy attributes of module "Patient Study"
[2022-10-18 18:05:54,501] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Study"
[2022-10-18 18:05:54,522] [WARNING] (root) - Tag SeriesDescription was not written, due to (0008, 103e)
[2022-10-18 18:05:54,628] [INFO] (__main__.AISpleenSegApp) - End run
Done performing execution of operator DICOMSegmentationWriterOperator

Once the application is verified inside Jupyter notebook, we can write the above Python code into Python files in an application folder.

The application folder structure would look like below:

my_app
├── __main__.py
└── app.py
# Create an application folder
!mkdir -p my_app

app.py

%%writefile my_app/app.py

# Copyright 2021-2022 MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging

# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.
from pydicom.sr.codedict import codes

from monai.deploy.core import Application, resource
from monai.deploy.core.domain import Image
from monai.deploy.core.io_type import IOType
from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator
from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator, SegmentDescription
from monai.deploy.operators.dicom_series_selector_operator import DICOMSeriesSelectorOperator
from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator
from monai.deploy.operators.monai_bundle_inference_operator import IOMapping, MonaiBundleInferenceOperator

# from monai.deploy.operators.stl_conversion_operator import STLConversionOperator  # import as needed.


@resource(cpu=1, gpu=1, memory="7Gi")
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
# The monai pkg is not required by this class, instead by the included operators.
class AISpleenSegApp(Application):
    def __init__(self, *args, **kwargs):
        """Creates an application instance."""
        self._logger = logging.getLogger("{}.{}".format(__name__, type(self).__name__))
        super().__init__(*args, **kwargs)

    def run(self, *args, **kwargs):
        # This method calls the base class to run. Can be omitted if simply calling through.
        self._logger.info(f"Begin {self.run.__name__}")
        super().run(*args, **kwargs)
        self._logger.info(f"End {self.run.__name__}")

    def compose(self):
        """Creates the app specific operators and chain them up in the processing DAG."""

        logging.info(f"Begin {self.compose.__name__}")

        # Create the custom operator(s) as well as SDK built-in operator(s).
        study_loader_op = DICOMDataLoaderOperator()
        series_selector_op = DICOMSeriesSelectorOperator(Sample_Rules_Text)
        series_to_vol_op = DICOMSeriesToVolumeOperator()

        # Create the inference operator that supports MONAI Bundle and automates the inference.
        # The IOMapping labels match the input and prediction keys in the pre and post processing.
        # The model_name is optional when the app has only one model.
        # The bundle_path argument optionally can be set to an accessible bundle file path in the dev
        # environment, so when the app is packaged into a MAP, the operator can complete the bundle parsing
        # during init to provide the optional packages info, parsed from the bundle, to the packager
        # for it to install the packages in the MAP docker image.
        # Setting output IOType to DISK only works only for leaf operators, not the case in this example.
        #
        # Pertinent MONAI Bundle:
        #   https://github.com/Project-MONAI/model-zoo/tree/dev/models/spleen_ct_segmentation
        bundle_spleen_seg_op = MonaiBundleInferenceOperator(
            input_mapping=[IOMapping("image", Image, IOType.IN_MEMORY)],
            output_mapping=[IOMapping("pred", Image, IOType.IN_MEMORY)],
        )

        # Create DICOM Seg writer providing the required segment description for each segment with
        # the actual algorithm and the pertinent organ/tissue. The segment_label, algorithm_name,
        # and algorithm_version are of DICOM VR LO type, limited to 64 chars.
        # https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
        segment_descriptions = [
            SegmentDescription(
                segment_label="Spleen",
                segmented_property_category=codes.SCT.Organ,
                segmented_property_type=codes.SCT.Spleen,
                algorithm_name="volumetric (3D) segmentation of the spleen from CT image",
                algorithm_family=codes.DCM.ArtificialIntelligence,
                algorithm_version="0.1.0",
            )
        ]
        custom_tags = {"SeriesDescription": "AI generated Seg, not for clinical use."}

        dicom_seg_writer = DICOMSegmentationWriterOperator(
            segment_descriptions=segment_descriptions, custom_tags=custom_tags
        )

        # Create the processing pipeline, by specifying the source and destination operators, and
        # ensuring the output from the former matches the input of the latter, in both name and type.
        self.add_flow(study_loader_op, series_selector_op, {"dicom_study_list": "dicom_study_list"})
        self.add_flow(
            series_selector_op, series_to_vol_op, {"study_selected_series_list": "study_selected_series_list"}
        )
        self.add_flow(series_to_vol_op, bundle_spleen_seg_op, {"image": "image"})
        # Note below the dicom_seg_writer requires two inputs, each coming from a source operator.
        self.add_flow(
            series_selector_op, dicom_seg_writer, {"study_selected_series_list": "study_selected_series_list"}
        )
        self.add_flow(bundle_spleen_seg_op, dicom_seg_writer, {"pred": "seg_image"})
        # Create the surface mesh STL conversion operator and add it to the app execution flow, if needed, by
        # uncommenting the following couple lines.
        # stl_conversion_op = STLConversionOperator(output_file="stl/spleen.stl")
        # self.add_flow(bundle_spleen_seg_op, stl_conversion_op, {"pred": "image"})

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


# This is a sample series selection rule in JSON, simply selecting CT series.
# If the study has more than 1 CT series, then all of them will be selected.
# Please see more detail in DICOMSeriesSelectorOperator.
Sample_Rules_Text = """
{
    "selections": [
        {
            "name": "CT Series",
            "conditions": {
                "StudyDescription": "(.*?)",
                "Modality": "(?i)CT",
                "SeriesDescription": "(.*?)"
            }
        }
    ]
}
"""

if __name__ == "__main__":
    # Creates the app and test it standalone. When running is this mode, please note the following:
    #     -m <model file>, for model file path
    #     -i <DICOM folder>, for input DICOM CT series folder
    #     -o <output folder>, for the output folder, default $PWD/output
    # e.g.
    #     monai-deploy exec app.py -i input -m model/model.ts
    #
    logging.basicConfig(level=logging.DEBUG)
    app_instance = AISpleenSegApp(do_run=True)
Overwriting my_app/app.py
if __name__ == "__main__":
    AISpleenSegApp(do_run=True)

The above lines are needed to execute the application code by using python interpreter.

__main__.py

__main__.py is needed for MONAI Application Packager to detect the main application code (app.py) when the application is executed with the application folder path (e.g., python simple_imaging_app).

%%writefile my_app/__main__.py
from app import AISpleenSegApp

if __name__ == "__main__":
    AISpleenSegApp(do_run=True)
Overwriting my_app/__main__.py
!ls my_app
app.py	__main__.py  __pycache__  spleen_seg_operator.py

In this time, let’s execute the app in the command line.

!python my_app -i dcm -o output -m model.ts
2022-10-18 18:05:59,753 - Begin compose
2022-10-18 18:05:59,754 - End compose
2022-10-18 18:05:59,754 - Begin run
Going to initiate execution of operator DICOMDataLoaderOperator
Executing operator DICOMDataLoaderOperator (Process ID: 1020777, Operator ID: 2771ad3e-33fb-4038-b88a-4d853d049a81)
Done performing execution of operator DICOMDataLoaderOperator

Going to initiate execution of operator DICOMSeriesSelectorOperator
Executing operator DICOMSeriesSelectorOperator (Process ID: 1020777, Operator ID: 44d22322-ff83-4a88-be70-5cc4223694e3)
[2022-10-18 18:06:00,668] [INFO] (root) - Finding series for Selection named: CT Series
[2022-10-18 18:06:00,668] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291
  # of series: 1
[2022-10-18 18:06:00,668] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-18 18:06:00,668] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2022-10-18 18:06:00,668] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST
[2022-10-18 18:06:00,668] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:00,668] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2022-10-18 18:06:00,668] [INFO] (root) -     Series attribute Modality value: CT
[2022-10-18 18:06:00,668] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:00,668] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'
[2022-10-18 18:06:00,668] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f
[2022-10-18 18:06:00,668] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:00,668] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-18 18:06:00,668] [INFO] (root) - Finding series for Selection named: CT Series
[2022-10-18 18:06:00,668] [INFO] (root) - Searching study, : 1.2.826.0.1.3680043.2.1125.1.67295333199898911264201812221946213
  # of series: 1
[2022-10-18 18:06:00,668] [INFO] (root) - Working on series, instance UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
[2022-10-18 18:06:00,668] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2022-10-18 18:06:00,668] [INFO] (root) -     Series attribute StudyDescription value: spleen
[2022-10-18 18:06:00,669] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:00,669] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2022-10-18 18:06:00,669] [INFO] (root) -     Series attribute Modality value: CT
[2022-10-18 18:06:00,669] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:00,669] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'
[2022-10-18 18:06:00,669] [INFO] (root) -     Series attribute SeriesDescription value: No series description
[2022-10-18 18:06:00,669] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:00,669] [INFO] (root) - Selected Series, UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
Done performing execution of operator DICOMSeriesSelectorOperator

Going to initiate execution of operator DICOMSeriesToVolumeOperator
Executing operator DICOMSeriesToVolumeOperator (Process ID: 1020777, Operator ID: c649ade1-971f-4e4d-8baf-64d9db612f10)
Done performing execution of operator DICOMSeriesToVolumeOperator

Going to initiate execution of operator MonaiBundleInferenceOperator
Executing operator MonaiBundleInferenceOperator (Process ID: 1020777, Operator ID: d0ff1fcb-96c5-4532-b8ec-86c6aba7b0a6)
Done performing execution of operator MonaiBundleInferenceOperator

Going to initiate execution of operator DICOMSegmentationWriterOperator
Executing operator DICOMSegmentationWriterOperator (Process ID: 1020777, Operator ID: 47727924-4ed9-4ffc-9b34-41ff48e62b1b)
/home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages/highdicom/valuerep.py:54: UserWarning: The string "C3N-00198" is unlikely to represent the intended person name since it contains only a single component. Construct a person name according to the format in described in http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html#sect_6.2.1.2, or, in pydicom 2.2.0 or later, use the pydicom.valuerep.PersonName.from_named_components() method to construct the person name correctly. If a single-component name is really intended, add a trailing caret character to disambiguate the name.
  warnings.warn(
[2022-10-18 18:06:16,288] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1
[2022-10-18 18:06:16,289] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1
[2022-10-18 18:06:16,290] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1
[2022-10-18 18:06:16,292] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1
[2022-10-18 18:06:16,293] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1
[2022-10-18 18:06:16,294] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1
[2022-10-18 18:06:16,295] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1
[2022-10-18 18:06:16,296] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1
[2022-10-18 18:06:16,297] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1
[2022-10-18 18:06:16,298] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1
[2022-10-18 18:06:16,299] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1
[2022-10-18 18:06:16,300] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1
[2022-10-18 18:06:16,301] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1
[2022-10-18 18:06:16,302] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1
[2022-10-18 18:06:16,303] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1
[2022-10-18 18:06:16,304] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1
[2022-10-18 18:06:16,305] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1
[2022-10-18 18:06:16,306] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1
[2022-10-18 18:06:16,307] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1
[2022-10-18 18:06:16,308] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1
[2022-10-18 18:06:16,309] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1
[2022-10-18 18:06:16,310] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1
[2022-10-18 18:06:16,311] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1
[2022-10-18 18:06:16,312] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1
[2022-10-18 18:06:16,313] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1
[2022-10-18 18:06:16,315] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1
[2022-10-18 18:06:16,316] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1
[2022-10-18 18:06:16,317] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1
[2022-10-18 18:06:16,318] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1
[2022-10-18 18:06:16,319] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1
[2022-10-18 18:06:16,320] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1
[2022-10-18 18:06:16,321] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1
[2022-10-18 18:06:16,322] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1
[2022-10-18 18:06:16,324] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1
[2022-10-18 18:06:16,325] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1
[2022-10-18 18:06:16,326] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1
[2022-10-18 18:06:16,327] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1
[2022-10-18 18:06:16,328] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1
[2022-10-18 18:06:16,329] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1
[2022-10-18 18:06:16,330] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1
[2022-10-18 18:06:16,331] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1
[2022-10-18 18:06:16,332] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1
[2022-10-18 18:06:16,333] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1
[2022-10-18 18:06:16,334] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1
[2022-10-18 18:06:16,335] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1
[2022-10-18 18:06:16,337] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1
[2022-10-18 18:06:16,338] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1
[2022-10-18 18:06:16,339] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1
[2022-10-18 18:06:16,340] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1
[2022-10-18 18:06:16,341] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1
[2022-10-18 18:06:16,342] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1
[2022-10-18 18:06:16,343] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1
[2022-10-18 18:06:16,344] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1
[2022-10-18 18:06:16,346] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1
[2022-10-18 18:06:16,347] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1
[2022-10-18 18:06:16,349] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1
[2022-10-18 18:06:16,350] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1
[2022-10-18 18:06:16,351] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1
[2022-10-18 18:06:16,353] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1
[2022-10-18 18:06:16,354] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1
[2022-10-18 18:06:16,355] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1
[2022-10-18 18:06:16,356] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1
[2022-10-18 18:06:16,358] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1
[2022-10-18 18:06:16,359] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1
[2022-10-18 18:06:16,360] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1
[2022-10-18 18:06:16,361] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1
[2022-10-18 18:06:16,362] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1
[2022-10-18 18:06:16,363] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1
[2022-10-18 18:06:16,364] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1
[2022-10-18 18:06:16,365] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1
[2022-10-18 18:06:16,366] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1
[2022-10-18 18:06:16,367] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1
[2022-10-18 18:06:16,368] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1
[2022-10-18 18:06:16,369] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1
[2022-10-18 18:06:16,370] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1
[2022-10-18 18:06:16,372] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1
[2022-10-18 18:06:16,373] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1
[2022-10-18 18:06:16,374] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1
[2022-10-18 18:06:16,375] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1
[2022-10-18 18:06:16,376] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1
[2022-10-18 18:06:16,377] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1
[2022-10-18 18:06:16,378] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1
[2022-10-18 18:06:16,380] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1
[2022-10-18 18:06:16,381] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1
[2022-10-18 18:06:16,382] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1
[2022-10-18 18:06:16,383] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1
[2022-10-18 18:06:16,384] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy Image-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy attributes of module "Specimen"
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy Patient-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy attributes of module "Patient"
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Subject"
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy Study-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:06:16,425] [INFO] (highdicom.base) - copy attributes of module "General Study"
[2022-10-18 18:06:16,426] [INFO] (highdicom.base) - copy attributes of module "Patient Study"
[2022-10-18 18:06:16,426] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Study"
[2022-10-18 18:06:16,436] [WARNING] (root) - Tag SeriesDescription was not written, due to (0008, 103e)
Done performing execution of operator DICOMSegmentationWriterOperator

[2022-10-18 18:06:16,521] [INFO] (app.AISpleenSegApp) - End run

Above command is same with the following command line:

import os
os.environ['MKL_THREADING_LAYER'] = 'GNU'
!monai-deploy exec my_app -i dcm -o output -m model.ts
2022-10-18 18:06:21,346 - Begin compose
2022-10-18 18:06:21,349 - End compose
2022-10-18 18:06:21,349 - Begin run
Going to initiate execution of operator DICOMDataLoaderOperator
Executing operator DICOMDataLoaderOperator (Process ID: 1020839, Operator ID: cd6de9f0-8a20-4c8e-ac40-e5bc1fa7c7e2)
Done performing execution of operator DICOMDataLoaderOperator

Going to initiate execution of operator DICOMSeriesSelectorOperator
Executing operator DICOMSeriesSelectorOperator (Process ID: 1020839, Operator ID: 99f613a7-c853-4076-bc0e-e12db550cb78)
[2022-10-18 18:06:22,381] [INFO] (root) - Finding series for Selection named: CT Series
[2022-10-18 18:06:22,381] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291
  # of series: 1
[2022-10-18 18:06:22,382] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-18 18:06:22,382] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2022-10-18 18:06:22,382] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST
[2022-10-18 18:06:22,382] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:22,382] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2022-10-18 18:06:22,382] [INFO] (root) -     Series attribute Modality value: CT
[2022-10-18 18:06:22,382] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:22,382] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'
[2022-10-18 18:06:22,382] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f
[2022-10-18 18:06:22,382] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:22,382] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-18 18:06:22,382] [INFO] (root) - Finding series for Selection named: CT Series
[2022-10-18 18:06:22,382] [INFO] (root) - Searching study, : 1.2.826.0.1.3680043.2.1125.1.67295333199898911264201812221946213
  # of series: 1
[2022-10-18 18:06:22,382] [INFO] (root) - Working on series, instance UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
[2022-10-18 18:06:22,382] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2022-10-18 18:06:22,382] [INFO] (root) -     Series attribute StudyDescription value: spleen
[2022-10-18 18:06:22,382] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:22,382] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2022-10-18 18:06:22,382] [INFO] (root) -     Series attribute Modality value: CT
[2022-10-18 18:06:22,382] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:22,382] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'
[2022-10-18 18:06:22,382] [INFO] (root) -     Series attribute SeriesDescription value: No series description
[2022-10-18 18:06:22,382] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-18 18:06:22,382] [INFO] (root) - Selected Series, UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
Done performing execution of operator DICOMSeriesSelectorOperator

Going to initiate execution of operator DICOMSeriesToVolumeOperator
Executing operator DICOMSeriesToVolumeOperator (Process ID: 1020839, Operator ID: 2e372081-a30f-44f2-bc7d-8ba10cee8708)
Done performing execution of operator DICOMSeriesToVolumeOperator

Going to initiate execution of operator MonaiBundleInferenceOperator
Executing operator MonaiBundleInferenceOperator (Process ID: 1020839, Operator ID: 15b8db9a-fa17-4291-b1c3-f61716d6bfa3)
Done performing execution of operator MonaiBundleInferenceOperator

Going to initiate execution of operator DICOMSegmentationWriterOperator
Executing operator DICOMSegmentationWriterOperator (Process ID: 1020839, Operator ID: 86e7f92c-d707-4cf2-8ce6-95c72c0205ef)
/home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages/highdicom/valuerep.py:54: UserWarning: The string "C3N-00198" is unlikely to represent the intended person name since it contains only a single component. Construct a person name according to the format in described in http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html#sect_6.2.1.2, or, in pydicom 2.2.0 or later, use the pydicom.valuerep.PersonName.from_named_components() method to construct the person name correctly. If a single-component name is really intended, add a trailing caret character to disambiguate the name.
  warnings.warn(
[2022-10-18 18:06:37,911] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1
[2022-10-18 18:06:37,913] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1
[2022-10-18 18:06:37,914] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1
[2022-10-18 18:06:37,915] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1
[2022-10-18 18:06:37,916] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1
[2022-10-18 18:06:37,917] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1
[2022-10-18 18:06:37,918] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1
[2022-10-18 18:06:37,919] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1
[2022-10-18 18:06:37,920] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1
[2022-10-18 18:06:37,921] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1
[2022-10-18 18:06:37,922] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1
[2022-10-18 18:06:37,923] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1
[2022-10-18 18:06:37,924] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1
[2022-10-18 18:06:37,925] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1
[2022-10-18 18:06:37,926] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1
[2022-10-18 18:06:37,927] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1
[2022-10-18 18:06:37,928] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1
[2022-10-18 18:06:37,929] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1
[2022-10-18 18:06:37,930] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1
[2022-10-18 18:06:37,931] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1
[2022-10-18 18:06:37,932] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1
[2022-10-18 18:06:37,933] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1
[2022-10-18 18:06:37,934] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1
[2022-10-18 18:06:37,935] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1
[2022-10-18 18:06:37,936] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1
[2022-10-18 18:06:37,937] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1
[2022-10-18 18:06:37,938] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1
[2022-10-18 18:06:37,939] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1
[2022-10-18 18:06:37,940] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1
[2022-10-18 18:06:37,941] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1
[2022-10-18 18:06:37,942] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1
[2022-10-18 18:06:37,943] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1
[2022-10-18 18:06:37,945] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1
[2022-10-18 18:06:37,946] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1
[2022-10-18 18:06:37,947] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1
[2022-10-18 18:06:37,948] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1
[2022-10-18 18:06:37,949] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1
[2022-10-18 18:06:37,950] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1
[2022-10-18 18:06:37,951] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1
[2022-10-18 18:06:37,952] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1
[2022-10-18 18:06:37,953] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1
[2022-10-18 18:06:37,954] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1
[2022-10-18 18:06:37,955] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1
[2022-10-18 18:06:37,956] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1
[2022-10-18 18:06:37,957] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1
[2022-10-18 18:06:37,958] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1
[2022-10-18 18:06:37,960] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1
[2022-10-18 18:06:37,961] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1
[2022-10-18 18:06:37,962] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1
[2022-10-18 18:06:37,963] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1
[2022-10-18 18:06:37,964] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1
[2022-10-18 18:06:37,966] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1
[2022-10-18 18:06:37,967] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1
[2022-10-18 18:06:37,968] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1
[2022-10-18 18:06:37,969] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1
[2022-10-18 18:06:37,970] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1
[2022-10-18 18:06:37,972] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1
[2022-10-18 18:06:37,973] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1
[2022-10-18 18:06:37,974] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1
[2022-10-18 18:06:37,975] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1
[2022-10-18 18:06:37,976] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1
[2022-10-18 18:06:37,977] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1
[2022-10-18 18:06:37,978] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1
[2022-10-18 18:06:37,980] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1
[2022-10-18 18:06:37,981] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1
[2022-10-18 18:06:37,982] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1
[2022-10-18 18:06:37,983] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1
[2022-10-18 18:06:37,984] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1
[2022-10-18 18:06:37,985] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1
[2022-10-18 18:06:37,986] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1
[2022-10-18 18:06:37,987] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1
[2022-10-18 18:06:37,989] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1
[2022-10-18 18:06:37,990] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1
[2022-10-18 18:06:37,991] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1
[2022-10-18 18:06:37,992] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1
[2022-10-18 18:06:37,993] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1
[2022-10-18 18:06:37,994] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1
[2022-10-18 18:06:37,995] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1
[2022-10-18 18:06:37,996] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1
[2022-10-18 18:06:37,998] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1
[2022-10-18 18:06:37,999] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1
[2022-10-18 18:06:38,000] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1
[2022-10-18 18:06:38,001] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1
[2022-10-18 18:06:38,002] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1
[2022-10-18 18:06:38,003] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1
[2022-10-18 18:06:38,004] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1
[2022-10-18 18:06:38,005] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1
[2022-10-18 18:06:38,045] [INFO] (highdicom.base) - copy Image-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy attributes of module "Specimen"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy Patient-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy attributes of module "Patient"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Subject"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy Study-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy attributes of module "General Study"
[2022-10-18 18:06:38,046] [INFO] (highdicom.base) - copy attributes of module "Patient Study"
[2022-10-18 18:06:38,047] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Study"
[2022-10-18 18:06:38,057] [WARNING] (root) - Tag SeriesDescription was not written, due to (0008, 103e)
Done performing execution of operator DICOMSegmentationWriterOperator

[2022-10-18 18:06:38,143] [INFO] (app.AISpleenSegApp) - End run
!ls output
1.2.826.0.1.3680043.10.511.3.12041615526490041922994303524987331.dcm
1.2.826.0.1.3680043.10.511.3.46164851841087364253778499084246156.dcm
1.2.826.0.1.3680043.10.511.3.59253586144990251681167260565313540.dcm

Packaging app

Let’s package the app with MONAI Application Packager.

!monai-deploy package -b nvcr.io/nvidia/pytorch:22.08-py3 my_app --tag my_app:latest -m model.ts
[2022-10-18 18:06:43,671] [INFO] (root) - Begin compose
[2022-10-18 18:06:43,673] [INFO] (root) - End compose
Building MONAI Application Package... Done
[2022-10-18 18:07:05,945] [INFO] (app_packager) - Successfully built my_app:latest

Note

Building a MONAI Application Package (Docker image) can take time. Use -l DEBUG option if you want to see the progress.

We can see that the Docker image is created.

!docker image ls | grep my_app
my_app                                                                latest                                     cb63d46f6b85   4 seconds ago       15.1GB

Executing packaged app locally

The packaged app can be run locally through MONAI Application Runner.

# Copy DICOM files are in 'dcm' folder

# Launch the app
!monai-deploy run my_app:latest dcm output
Checking dependencies...
--> Verifying if "docker" is installed...

--> Verifying if "my_app:latest" is available...

Checking for MAP "my_app:latest" locally
"my_app:latest" found.

Reading MONAI App Package manifest...
--> Verifying if "nvidia-docker" is installed...

/opt/conda/lib/python3.8/site-packages/scipy/__init__.py:138: UserWarning: A NumPy version >=1.16.5 and <1.23.0 is required for this version of SciPy (detected version 1.23.4)
  warnings.warn(f"A NumPy version >={np_minversion} and <{np_maxversion} is required for this version of "
2022-10-19 01:07:20,302 - Begin compose
2022-10-19 01:07:20,304 - End compose
2022-10-19 01:07:20,304 - Begin run
Going to initiate execution of operator DICOMDataLoaderOperator
Executing operator DICOMDataLoaderOperator (Process ID: 1, Operator ID: 2b5b3f81-8010-442f-b119-85c5ed42a6a6)
Done performing execution of operator DICOMDataLoaderOperator

Going to initiate execution of operator DICOMSeriesSelectorOperator
Executing operator DICOMSeriesSelectorOperator (Process ID: 1, Operator ID: 9f226d1c-84cb-4569-9067-195ea5ed5248)
[2022-10-19 01:07:21,460] [INFO] (root) - Finding series for Selection named: CT Series
[2022-10-19 01:07:21,460] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291
  # of series: 1
[2022-10-19 01:07:21,460] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-19 01:07:21,461] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2022-10-19 01:07:21,461] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST
[2022-10-19 01:07:21,461] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-19 01:07:21,462] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2022-10-19 01:07:21,462] [INFO] (root) -     Series attribute Modality value: CT
[2022-10-19 01:07:21,462] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-19 01:07:21,462] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'
[2022-10-19 01:07:21,462] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f
[2022-10-19 01:07:21,462] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-19 01:07:21,462] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239
[2022-10-19 01:07:21,462] [INFO] (root) - Finding series for Selection named: CT Series
[2022-10-19 01:07:21,462] [INFO] (root) - Searching study, : 1.2.826.0.1.3680043.2.1125.1.67295333199898911264201812221946213
  # of series: 1
[2022-10-19 01:07:21,462] [INFO] (root) - Working on series, instance UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
[2022-10-19 01:07:21,462] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'
[2022-10-19 01:07:21,462] [INFO] (root) -     Series attribute StudyDescription value: spleen
[2022-10-19 01:07:21,462] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-19 01:07:21,462] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'
[2022-10-19 01:07:21,463] [INFO] (root) -     Series attribute Modality value: CT
[2022-10-19 01:07:21,463] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-19 01:07:21,463] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'
[2022-10-19 01:07:21,463] [INFO] (root) -     Series attribute SeriesDescription value: No series description
[2022-10-19 01:07:21,463] [INFO] (root) - Series attribute string value did not match. Try regEx.
[2022-10-19 01:07:21,463] [INFO] (root) - Selected Series, UID: 1.2.826.0.1.3680043.2.1125.1.68102559796966796813942775094416763
Done performing execution of operator DICOMSeriesSelectorOperator

Going to initiate execution of operator DICOMSeriesToVolumeOperator
Executing operator DICOMSeriesToVolumeOperator (Process ID: 1, Operator ID: 3b219d8c-e5aa-4314-a002-a92a78aa247d)
Done performing execution of operator DICOMSeriesToVolumeOperator

Going to initiate execution of operator MonaiBundleInferenceOperator
Executing operator MonaiBundleInferenceOperator (Process ID: 1, Operator ID: 832ca868-4163-4a99-aa28-542908a26b79)
Done performing execution of operator MonaiBundleInferenceOperator

Going to initiate execution of operator DICOMSegmentationWriterOperator
Executing operator DICOMSegmentationWriterOperator (Process ID: 1, Operator ID: c7867e7d-5bc8-45ed-a368-7965e49ea8b1)
/root/.local/lib/python3.8/site-packages/highdicom/valuerep.py:54: UserWarning: The string "C3N-00198" is unlikely to represent the intended person name since it contains only a single component. Construct a person name according to the format in described in http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html#sect_6.2.1.2, or, in pydicom 2.2.0 or later, use the pydicom.valuerep.PersonName.from_named_components() method to construct the person name correctly. If a single-component name is really intended, add a trailing caret character to disambiguate the name.
  warnings.warn(
[2022-10-19 01:07:36,076] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1
[2022-10-19 01:07:36,078] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1
[2022-10-19 01:07:36,079] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1
[2022-10-19 01:07:36,081] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1
[2022-10-19 01:07:36,082] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1
[2022-10-19 01:07:36,084] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1
[2022-10-19 01:07:36,085] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1
[2022-10-19 01:07:36,086] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1
[2022-10-19 01:07:36,088] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1
[2022-10-19 01:07:36,089] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1
[2022-10-19 01:07:36,091] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1
[2022-10-19 01:07:36,092] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1
[2022-10-19 01:07:36,093] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1
[2022-10-19 01:07:36,095] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1
[2022-10-19 01:07:36,096] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1
[2022-10-19 01:07:36,098] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1
[2022-10-19 01:07:36,099] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1
[2022-10-19 01:07:36,101] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1
[2022-10-19 01:07:36,102] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1
[2022-10-19 01:07:36,103] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1
[2022-10-19 01:07:36,105] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1
[2022-10-19 01:07:36,106] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1
[2022-10-19 01:07:36,108] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1
[2022-10-19 01:07:36,109] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1
[2022-10-19 01:07:36,111] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1
[2022-10-19 01:07:36,112] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1
[2022-10-19 01:07:36,114] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1
[2022-10-19 01:07:36,115] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1
[2022-10-19 01:07:36,117] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1
[2022-10-19 01:07:36,118] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1
[2022-10-19 01:07:36,120] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1
[2022-10-19 01:07:36,121] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1
[2022-10-19 01:07:36,123] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1
[2022-10-19 01:07:36,125] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1
[2022-10-19 01:07:36,126] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1
[2022-10-19 01:07:36,128] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1
[2022-10-19 01:07:36,129] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1
[2022-10-19 01:07:36,130] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1
[2022-10-19 01:07:36,132] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1
[2022-10-19 01:07:36,133] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1
[2022-10-19 01:07:36,135] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1
[2022-10-19 01:07:36,136] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1
[2022-10-19 01:07:36,138] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1
[2022-10-19 01:07:36,139] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1
[2022-10-19 01:07:36,141] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1
[2022-10-19 01:07:36,142] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1
[2022-10-19 01:07:36,144] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1
[2022-10-19 01:07:36,145] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1
[2022-10-19 01:07:36,146] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1
[2022-10-19 01:07:36,148] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1
[2022-10-19 01:07:36,149] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1
[2022-10-19 01:07:36,151] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1
[2022-10-19 01:07:36,152] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1
[2022-10-19 01:07:36,154] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1
[2022-10-19 01:07:36,155] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1
[2022-10-19 01:07:36,157] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1
[2022-10-19 01:07:36,158] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1
[2022-10-19 01:07:36,160] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1
[2022-10-19 01:07:36,161] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1
[2022-10-19 01:07:36,163] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1
[2022-10-19 01:07:36,164] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1
[2022-10-19 01:07:36,166] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1
[2022-10-19 01:07:36,167] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1
[2022-10-19 01:07:36,169] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1
[2022-10-19 01:07:36,170] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1
[2022-10-19 01:07:36,172] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1
[2022-10-19 01:07:36,173] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1
[2022-10-19 01:07:36,175] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1
[2022-10-19 01:07:36,176] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1
[2022-10-19 01:07:36,178] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1
[2022-10-19 01:07:36,179] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1
[2022-10-19 01:07:36,181] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1
[2022-10-19 01:07:36,182] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1
[2022-10-19 01:07:36,184] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1
[2022-10-19 01:07:36,186] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1
[2022-10-19 01:07:36,187] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1
[2022-10-19 01:07:36,189] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1
[2022-10-19 01:07:36,190] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1
[2022-10-19 01:07:36,192] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1
[2022-10-19 01:07:36,193] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1
[2022-10-19 01:07:36,195] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1
[2022-10-19 01:07:36,196] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1
[2022-10-19 01:07:36,198] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1
[2022-10-19 01:07:36,199] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1
[2022-10-19 01:07:36,201] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1
[2022-10-19 01:07:36,203] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1
[2022-10-19 01:07:36,204] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1
[2022-10-19 01:07:36,243] [INFO] (highdicom.base) - copy Image-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-19 01:07:36,243] [INFO] (highdicom.base) - copy attributes of module "Specimen"
[2022-10-19 01:07:36,243] [INFO] (highdicom.base) - copy Patient-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-19 01:07:36,243] [INFO] (highdicom.base) - copy attributes of module "Patient"
[2022-10-19 01:07:36,244] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Subject"
[2022-10-19 01:07:36,244] [INFO] (highdicom.base) - copy Study-related attributes from dataset "1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191"
[2022-10-19 01:07:36,244] [INFO] (highdicom.base) - copy attributes of module "General Study"
[2022-10-19 01:07:36,244] [INFO] (highdicom.base) - copy attributes of module "Patient Study"
[2022-10-19 01:07:36,245] [INFO] (highdicom.base) - copy attributes of module "Clinical Trial Study"
[2022-10-19 01:07:36,262] [WARNING] (root) - Tag SeriesDescription was not written, due to (0008, 103e)
Done performing execution of operator DICOMSegmentationWriterOperator

[2022-10-19 01:07:36,385] [INFO] (__main__.AISpleenSegApp) - End run
!ls output
1.2.826.0.1.3680043.10.511.3.10344779550124226469948949035444422.dcm
1.2.826.0.1.3680043.10.511.3.12041615526490041922994303524987331.dcm
1.2.826.0.1.3680043.10.511.3.46164851841087364253778499084246156.dcm
1.2.826.0.1.3680043.10.511.3.59253586144990251681167260565313540.dcm