{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Creating a Segmentation App with MONAI Deploy App SDK\n",
    "\n",
    "This tutorial shows how to create an organ segmentation application for a PyTorch model that has been trained with MONAI. Please note that this tutorial is based on the [earlier version](https://github.com/Project-MONAI/monai-deploy-app-sdk/blob/7615d73f6ec2125ba5d2e3480f85b060e95b81e4/examples/apps/ai_spleen_seg_app/app.py) of the Spleen Segmentation Application.\n",
    "\n",
    "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.\n",
    "\n",
    "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 could be a DICOM Segmentation object or a DICOM RT Structure Set, and for classification, DICOM Structure Report and/or DICOM Encapsulated PDF.\n",
    "\n",
    "During model training, input and label images are typically in non-DICOM volumetric image format, e.g., NIfTI and PNG, converted from a specific DICOM study series. Furthermore, the voxel spacings most likely have been re-sampled to be uniform for all images. When integrated with imaging networks and receiving DICOM instances from modalities and Picture Archiving and Communications System, PACS, an AI deploy application may have 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.\n",
    "\n",
    "In the following sections, we will demonstrate how to create a MONAI Deploy application package using the MONAI Deploy App SDK.\n",
    "\n",
    ":::{note}\n",
    "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 NIfTI to DICOM files.\n",
    "\n",
    ":::\n",
    "\n",
    "## Creating Operators and connecting them in Application class\n",
    "\n",
    "We will implement an application that consists of five Operators:\n",
    "\n",
    "- **DICOMDataLoaderOperator**:\n",
    "    - **Input(dicom_files)**: a folder path ([`DataPath`](/modules/_autosummary/monai.deploy.core.domain.DataPath))\n",
    "    - **Output(dicom_study_list)**: a list of DICOM studies in memory (List[[`DICOMStudy`](/modules/_autosummary/monai.deploy.core.domain.DICOMStudy)])\n",
    "- **DICOMSeriesSelectorOperator**:\n",
    "    - **Input(dicom_study_list)**: a list of DICOM studies in memory (List[[`DICOMStudy`](/modules/_autosummary/monai.deploy.core.domain.DICOMStudy)])\n",
    "    - **Input(selection_rules)**: a selection rule (Dict)\n",
    "    - **Output(study_selected_series_list)**: a DICOM series object in memory ([`StudySelectedSeries`](/modules/_autosummary/monai.deploy.core.domain.StudySelectedSeries))\n",
    "- **DICOMSeriesToVolumeOperator**:\n",
    "    - **Input(study_selected_series_list)**: a DICOM series object in memory ([`StudySelectedSeries`](/modules/_autosummary/monai.deploy.core.domain.StudySelectedSeries))\n",
    "    - **Output(image)**: an image object in memory ([`Image`](/modules/_autosummary/monai.deploy.core.domain.Image))\n",
    "- **SpleenSegOperator**:\n",
    "    - **Input(image)**: an image object in memory ([`Image`](/modules/_autosummary/monai.deploy.core.domain.Image))\n",
    "    - **Output(seg_image)**: an image object in memory ([`Image`](/modules/_autosummary/monai.deploy.core.domain.Image))\n",
    "- **DICOMSegmentationWriterOperator**:\n",
    "    - **Input(seg_image)**: a segmentation image object in memory ([`Image`](/modules/_autosummary/monai.deploy.core.domain.Image))\n",
    "    - **Input(study_selected_series_list)**: a DICOM series object in memory ([`StudySelectedSeries`](/modules/_autosummary/monai.deploy.core.domain.StudySelectedSeries))\n",
    "    - **Output(dicom_seg_instance)**: a file path ([`DataPath`](/modules/_autosummary/monai.deploy.core.domain.DataPath))\n",
    "\n",
    "\n",
    ":::{note}\n",
    "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.\n",
    ":::\n",
    "\n",
    "The workflow of the application would look like this.\n",
    "\n",
    "```{mermaid}\n",
    "%%{init: {\"theme\": \"base\", \"themeVariables\": { \"fontSize\": \"16px\"}} }%%\n",
    "\n",
    "classDiagram\n",
    "    direction TB\n",
    "\n",
    "    DICOMDataLoaderOperator --|> DICOMSeriesSelectorOperator : dicom_study_list...dicom_study_list\n",
    "    DICOMSeriesSelectorOperator --|> DICOMSeriesToVolumeOperator : study_selected_series_list...study_selected_series_list\n",
    "    DICOMSeriesToVolumeOperator --|> SpleenSegOperator : image...image\n",
    "    DICOMSeriesSelectorOperator --|> DICOMSegmentationWriterOperator : study_selected_series_list...study_selected_series_list\n",
    "    SpleenSegOperator --|> DICOMSegmentationWriterOperator : seg_image...seg_image\n",
    "\n",
    "\n",
    "    class DICOMDataLoaderOperator {\n",
    "        <in>dicom_files : DISK\n",
    "        dicom_study_list(out) IN_MEMORY\n",
    "    }\n",
    "    class DICOMSeriesSelectorOperator {\n",
    "        <in>dicom_study_list : IN_MEMORY\n",
    "        <in>selection_rules : IN_MEMORY\n",
    "        study_selected_series_list(out) IN_MEMORY\n",
    "    }\n",
    "    class DICOMSeriesToVolumeOperator {\n",
    "        <in>study_selected_series_list : IN_MEMORY\n",
    "        image(out) IN_MEMORY\n",
    "    }\n",
    "    class SpleenSegOperator {\n",
    "        <in>image : IN_MEMORY\n",
    "        seg_image(out) IN_MEMORY\n",
    "    }\n",
    "    class DICOMSegmentationWriterOperator {\n",
    "        <in>seg_image : IN_MEMORY\n",
    "        <in>study_selected_series_list : IN_MEMORY\n",
    "        dicom_seg_instance(out) DISK\n",
    "    }\n",
    "```\n",
    "\n",
    "### Setup environment\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install MONAI and other necessary image processing packages for the application\n",
    "!python -c \"import monai\" || pip install --upgrade -q \"monai\"\n",
    "!python -c \"import torch\" || pip install -q \"torch>=1.10.2\"\n",
    "!python -c \"import numpy\" || pip install -q \"numpy>=1.21\"\n",
    "!python -c \"import nibabel\" || pip install -q \"nibabel>=3.2.1\"\n",
    "!python -c \"import pydicom\" || pip install -q \"pydicom>=1.4.2\"\n",
    "!python -c \"import highdicom\" || pip install -q \"highdicom>=0.18.2\"\n",
    "!python -c \"import SimpleITK\" || pip install -q \"SimpleITK>=2.0.0\"\n",
    "!python -c \"import typeguard\" || pip install -q \"typeguard>=2.12.1\"\n",
    "\n",
    "# Install MONAI Deploy App SDK package\n",
    "!python -c \"import monai.deploy\" || pip install --upgrade -q \"monai-deploy-app-sdk\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: you may need to restart the Jupyter kernel to use the updated packages."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Download/Extract ai_spleen_bundle_data from Google Drive"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: gdown in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (4.5.1)\n",
      "Requirement already satisfied: six in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (1.16.0)\n",
      "Requirement already satisfied: tqdm in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (4.64.0)\n",
      "Requirement already satisfied: beautifulsoup4 in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (4.11.1)\n",
      "Requirement already satisfied: requests[socks] in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (2.28.1)\n",
      "Requirement already satisfied: filelock in /home/mqin/src/monai-deploy-app-sdk/.venv/lib/python3.8/site-packages (from gdown) (3.8.0)\n",
      "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)\n",
      "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)\n",
      "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)\n",
      "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)\n",
      "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)\n",
      "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)\n",
      "Downloading...\n",
      "From: https://drive.google.com/uc?id=1Uds8mEvdGNYUuvFpTtCQ8gNU97bAPCaQ\n",
      "To: /home/mqin/src/monai-deploy-app-sdk/notebooks/tutorials/ai_spleen_seg_bundle_data.zip\n",
      "100%|███████████████████████████████████████| 79.4M/79.4M [00:00<00:00, 105MB/s]\n",
      "Archive:  ai_spleen_seg_bundle_data.zip\n",
      "  inflating: dcm/1-001.dcm           \n",
      "  inflating: dcm/1-002.dcm           \n",
      "  inflating: dcm/1-003.dcm           \n",
      "  inflating: dcm/1-004.dcm           \n",
      "  inflating: dcm/1-005.dcm           \n",
      "  inflating: dcm/1-006.dcm           \n",
      "  inflating: dcm/1-007.dcm           \n",
      "  inflating: dcm/1-008.dcm           \n",
      "  inflating: dcm/1-009.dcm           \n",
      "  inflating: dcm/1-010.dcm           \n",
      "  inflating: dcm/1-011.dcm           \n",
      "  inflating: dcm/1-012.dcm           \n",
      "  inflating: dcm/1-013.dcm           \n",
      "  inflating: dcm/1-014.dcm           \n",
      "  inflating: dcm/1-015.dcm           \n",
      "  inflating: dcm/1-016.dcm           \n",
      "  inflating: dcm/1-017.dcm           \n",
      "  inflating: dcm/1-018.dcm           \n",
      "  inflating: dcm/1-019.dcm           \n",
      "  inflating: dcm/1-020.dcm           \n",
      "  inflating: dcm/1-021.dcm           \n",
      "  inflating: dcm/1-022.dcm           \n",
      "  inflating: dcm/1-023.dcm           \n",
      "  inflating: dcm/1-024.dcm           \n",
      "  inflating: dcm/1-025.dcm           \n",
      "  inflating: dcm/1-026.dcm           \n",
      "  inflating: dcm/1-027.dcm           \n",
      "  inflating: dcm/1-028.dcm           \n",
      "  inflating: dcm/1-029.dcm           \n",
      "  inflating: dcm/1-030.dcm           \n",
      "  inflating: dcm/1-031.dcm           \n",
      "  inflating: dcm/1-032.dcm           \n",
      "  inflating: dcm/1-033.dcm           \n",
      "  inflating: dcm/1-034.dcm           \n",
      "  inflating: dcm/1-035.dcm           \n",
      "  inflating: dcm/1-036.dcm           \n",
      "  inflating: dcm/1-037.dcm           \n",
      "  inflating: dcm/1-038.dcm           \n",
      "  inflating: dcm/1-039.dcm           \n",
      "  inflating: dcm/1-040.dcm           \n",
      "  inflating: dcm/1-041.dcm           \n",
      "  inflating: dcm/1-042.dcm           \n",
      "  inflating: dcm/1-043.dcm           \n",
      "  inflating: dcm/1-044.dcm           \n",
      "  inflating: dcm/1-045.dcm           \n",
      "  inflating: dcm/1-046.dcm           \n",
      "  inflating: dcm/1-047.dcm           \n",
      "  inflating: dcm/1-048.dcm           \n",
      "  inflating: dcm/1-049.dcm           \n",
      "  inflating: dcm/1-050.dcm           \n",
      "  inflating: dcm/1-051.dcm           \n",
      "  inflating: dcm/1-052.dcm           \n",
      "  inflating: dcm/1-053.dcm           \n",
      "  inflating: dcm/1-054.dcm           \n",
      "  inflating: dcm/1-055.dcm           \n",
      "  inflating: dcm/1-056.dcm           \n",
      "  inflating: dcm/1-057.dcm           \n",
      "  inflating: dcm/1-058.dcm           \n",
      "  inflating: dcm/1-059.dcm           \n",
      "  inflating: dcm/1-060.dcm           \n",
      "  inflating: dcm/1-061.dcm           \n",
      "  inflating: dcm/1-062.dcm           \n",
      "  inflating: dcm/1-063.dcm           \n",
      "  inflating: dcm/1-064.dcm           \n",
      "  inflating: dcm/1-065.dcm           \n",
      "  inflating: dcm/1-066.dcm           \n",
      "  inflating: dcm/1-067.dcm           \n",
      "  inflating: dcm/1-068.dcm           \n",
      "  inflating: dcm/1-069.dcm           \n",
      "  inflating: dcm/1-070.dcm           \n",
      "  inflating: dcm/1-071.dcm           \n",
      "  inflating: dcm/1-072.dcm           \n",
      "  inflating: dcm/1-073.dcm           \n",
      "  inflating: dcm/1-074.dcm           \n",
      "  inflating: dcm/1-075.dcm           \n",
      "  inflating: dcm/1-076.dcm           \n",
      "  inflating: dcm/1-077.dcm           \n",
      "  inflating: dcm/1-078.dcm           \n",
      "  inflating: dcm/1-079.dcm           \n",
      "  inflating: dcm/1-080.dcm           \n",
      "  inflating: dcm/1-081.dcm           \n",
      "  inflating: dcm/1-082.dcm           \n",
      "  inflating: dcm/1-083.dcm           \n",
      "  inflating: dcm/1-084.dcm           \n",
      "  inflating: dcm/1-085.dcm           \n",
      "  inflating: dcm/1-086.dcm           \n",
      "  inflating: dcm/1-087.dcm           \n",
      "  inflating: dcm/1-088.dcm           \n",
      "  inflating: dcm/1-089.dcm           \n",
      "  inflating: dcm/1-090.dcm           \n",
      "  inflating: dcm/1-091.dcm           \n",
      "  inflating: dcm/1-092.dcm           \n",
      "  inflating: dcm/1-093.dcm           \n",
      "  inflating: dcm/1-094.dcm           \n",
      "  inflating: dcm/1-095.dcm           \n",
      "  inflating: dcm/1-096.dcm           \n",
      "  inflating: dcm/1-097.dcm           \n",
      "  inflating: dcm/1-098.dcm           \n",
      "  inflating: dcm/1-099.dcm           \n",
      "  inflating: dcm/1-100.dcm           \n",
      "  inflating: dcm/1-101.dcm           \n",
      "  inflating: dcm/1-102.dcm           \n",
      "  inflating: dcm/1-103.dcm           \n",
      "  inflating: dcm/1-104.dcm           \n",
      "  inflating: dcm/1-105.dcm           \n",
      "  inflating: dcm/1-106.dcm           \n",
      "  inflating: dcm/1-107.dcm           \n",
      "  inflating: dcm/1-108.dcm           \n",
      "  inflating: dcm/1-109.dcm           \n",
      "  inflating: dcm/1-110.dcm           \n",
      "  inflating: dcm/1-111.dcm           \n",
      "  inflating: dcm/1-112.dcm           \n",
      "  inflating: dcm/1-113.dcm           \n",
      "  inflating: dcm/1-114.dcm           \n",
      "  inflating: dcm/1-115.dcm           \n",
      "  inflating: dcm/1-116.dcm           \n",
      "  inflating: dcm/1-117.dcm           \n",
      "  inflating: dcm/1-118.dcm           \n",
      "  inflating: dcm/1-119.dcm           \n",
      "  inflating: dcm/1-120.dcm           \n",
      "  inflating: dcm/1-121.dcm           \n",
      "  inflating: dcm/1-122.dcm           \n",
      "  inflating: dcm/1-123.dcm           \n",
      "  inflating: dcm/1-124.dcm           \n",
      "  inflating: dcm/1-125.dcm           \n",
      "  inflating: dcm/1-126.dcm           \n",
      "  inflating: dcm/1-127.dcm           \n",
      "  inflating: dcm/1-128.dcm           \n",
      "  inflating: dcm/1-129.dcm           \n",
      "  inflating: dcm/1-130.dcm           \n",
      "  inflating: dcm/1-131.dcm           \n",
      "  inflating: dcm/1-132.dcm           \n",
      "  inflating: dcm/1-133.dcm           \n",
      "  inflating: dcm/1-134.dcm           \n",
      "  inflating: dcm/1-135.dcm           \n",
      "  inflating: dcm/1-136.dcm           \n",
      "  inflating: dcm/1-137.dcm           \n",
      "  inflating: dcm/1-138.dcm           \n",
      "  inflating: dcm/1-139.dcm           \n",
      "  inflating: dcm/1-140.dcm           \n",
      "  inflating: dcm/1-141.dcm           \n",
      "  inflating: dcm/1-142.dcm           \n",
      "  inflating: dcm/1-143.dcm           \n",
      "  inflating: dcm/1-144.dcm           \n",
      "  inflating: dcm/1-145.dcm           \n",
      "  inflating: dcm/1-146.dcm           \n",
      "  inflating: dcm/1-147.dcm           \n",
      "  inflating: dcm/1-148.dcm           \n",
      "  inflating: dcm/1-149.dcm           \n",
      "  inflating: dcm/1-150.dcm           \n",
      "  inflating: dcm/1-151.dcm           \n",
      "  inflating: dcm/1-152.dcm           \n",
      "  inflating: dcm/1-153.dcm           \n",
      "  inflating: dcm/1-154.dcm           \n",
      "  inflating: dcm/1-155.dcm           \n",
      "  inflating: dcm/1-156.dcm           \n",
      "  inflating: dcm/1-157.dcm           \n",
      "  inflating: dcm/1-158.dcm           \n",
      "  inflating: dcm/1-159.dcm           \n",
      "  inflating: dcm/1-160.dcm           \n",
      "  inflating: dcm/1-161.dcm           \n",
      "  inflating: dcm/1-162.dcm           \n",
      "  inflating: dcm/1-163.dcm           \n",
      "  inflating: dcm/1-164.dcm           \n",
      "  inflating: dcm/1-165.dcm           \n",
      "  inflating: dcm/1-166.dcm           \n",
      "  inflating: dcm/1-167.dcm           \n",
      "  inflating: dcm/1-168.dcm           \n",
      "  inflating: dcm/1-169.dcm           \n",
      "  inflating: dcm/1-170.dcm           \n",
      "  inflating: dcm/1-171.dcm           \n",
      "  inflating: dcm/1-172.dcm           \n",
      "  inflating: dcm/1-173.dcm           \n",
      "  inflating: dcm/1-174.dcm           \n",
      "  inflating: dcm/1-175.dcm           \n",
      "  inflating: dcm/1-176.dcm           \n",
      "  inflating: dcm/1-177.dcm           \n",
      "  inflating: dcm/1-178.dcm           \n",
      "  inflating: dcm/1-179.dcm           \n",
      "  inflating: dcm/1-180.dcm           \n",
      "  inflating: dcm/1-181.dcm           \n",
      "  inflating: dcm/1-182.dcm           \n",
      "  inflating: dcm/1-183.dcm           \n",
      "  inflating: dcm/1-184.dcm           \n",
      "  inflating: dcm/1-185.dcm           \n",
      "  inflating: dcm/1-186.dcm           \n",
      "  inflating: dcm/1-187.dcm           \n",
      "  inflating: dcm/1-188.dcm           \n",
      "  inflating: dcm/1-189.dcm           \n",
      "  inflating: dcm/1-190.dcm           \n",
      "  inflating: dcm/1-191.dcm           \n",
      "  inflating: dcm/1-192.dcm           \n",
      "  inflating: dcm/1-193.dcm           \n",
      "  inflating: dcm/1-194.dcm           \n",
      "  inflating: dcm/1-195.dcm           \n",
      "  inflating: dcm/1-196.dcm           \n",
      "  inflating: dcm/1-197.dcm           \n",
      "  inflating: dcm/1-198.dcm           \n",
      "  inflating: dcm/1-199.dcm           \n",
      "  inflating: dcm/1-200.dcm           \n",
      "  inflating: dcm/1-201.dcm           \n",
      "  inflating: dcm/1-202.dcm           \n",
      "  inflating: dcm/1-203.dcm           \n",
      "  inflating: dcm/1-204.dcm           \n",
      "  inflating: model.ts                \n"
     ]
    }
   ],
   "source": [
    "# Download ai_spleen_bundle_data test data zip file\n",
    "!pip install gdown \n",
    "!gdown \"https://drive.google.com/uc?id=1Uds8mEvdGNYUuvFpTtCQ8gNU97bAPCaQ\"\n",
    "\n",
    "# After downloading ai_spleen_bundle_data zip file from the web browser or using gdown,\n",
    "!unzip -o \"ai_spleen_seg_bundle_data.zip\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup imports\n",
    "\n",
    "Let's import necessary classes/decorators to define Application and Operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "from os import path\n",
    "\n",
    "from numpy import uint8\n",
    "\n",
    "import monai.deploy.core as md\n",
    "from monai.deploy.core import ExecutionContext, Image, InputContext, IOType, Operator, OutputContext\n",
    "from monai.deploy.operators.monai_seg_inference_operator import InMemImageReader, MonaiSegInferenceOperator\n",
    "from monai.transforms import (\n",
    "    Activationsd,\n",
    "    AsDiscreted,\n",
    "    Compose,\n",
    "    EnsureChannelFirstd,\n",
    "    EnsureTyped,\n",
    "    Invertd,\n",
    "    LoadImaged,\n",
    "    Orientationd,\n",
    "    SaveImaged,\n",
    "    ScaleIntensityRanged,\n",
    "    Spacingd,\n",
    ")\n",
    "\n",
    "# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.\n",
    "from pydicom.sr.codedict import codes\n",
    "\n",
    "from monai.deploy.core import Application, resource\n",
    "from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator\n",
    "from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator, SegmentDescription\n",
    "from monai.deploy.operators.dicom_series_selector_operator import DICOMSeriesSelectorOperator\n",
    "from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating Model Specific Inference Operator classes\n",
    "\n",
    "Each Operator class inherits [Operator](/modules/_autosummary/monai.deploy.core.Operator) class and input/output properties are specified by using [@input](/modules/_autosummary/monai.deploy.core.input)/[@output](/modules/_autosummary/monai.deploy.core.output) decorators.\n",
    "\n",
    "Business logic would be implemented in the <a href=\"../../modules/_autosummary/monai.deploy.core.Operator.html#monai.deploy.core.Operator.compute\">compute()</a> method.\n",
    "\n",
    "The App SDK provides a `MonaiSegInferenceOperator` class to perform segmentation prediction with a Torch Script model. For consistency, this class uses MONAI dictionary-based transforms, as `Compose` object, for pre and post transforms. The model-specific inference operator will then only need to create the pre and post transform `Compose` based on what has been used in the model training and validation. Note that for deploy application, `ignite` is not needed nor supported.\n",
    "\n",
    "#### SpleenSegOperator\n",
    "\n",
    "The `SpleenSegOperator` gets as input an in-memory [Image](/modules/_autosummary/monai.deploy.core.domain.Image) object that has been converted from a DICOM CT series by the preceding `DICOMSeriesToVolumeOperator`, and as output in-memory segmentation [Image](/modules/_autosummary/monai.deploy.core.domain.Image) object.\n",
    "\n",
    "The `pre_process` function creates the pre-transforms `Compose` object. For `LoadImage`, a specialized `InMemImageReader`, derived from MONAI `ImageReader`, is used to convert the in-memory pixel data and return the `numpy` array as well as the meta-data. Also, the DICOM input pixel spacings are often not the same as expected by the model, so the `Spacingd` transform must be used to re-sample the image with the expected spacing.\n",
    "\n",
    "The `post_process` function creates the post-transform `Compose` object. The `SaveImageD` transform class is used to save the segmentation mask as NIfTI image file, which is optional as the in-memory mask image will be passed down to the DICOM Segmentation writer for creating a DICOM Segmentation instance. The `Invertd` must also be used to revert the segmentation image's orientation and spacing to be the same as the input.\n",
    "\n",
    "When the `MonaiSegInferenceOperator` object is created, the `ROI` size is specified, as well as the transform `Compose` objects. Furthermore, the dataset image key names are set accordingly.\n",
    "\n",
    "Loading of the model and performing the prediction are encapsulated in the `MonaiSegInferenceOperator` and other SDK classes. Once the inference is completed, the segmentation [Image](/modules/_autosummary/monai.deploy.core.domain.Image) object is created and set to the output (<a href=\"../../modules/_autosummary/monai.deploy.core.OutputContext.html#monai.deploy.core.OutputContext.set\">op_output.set(value, label)</a>), by the `MonaiSegInferenceOperator`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "@md.input(\"image\", Image, IOType.IN_MEMORY)\n",
    "@md.output(\"seg_image\", Image, IOType.IN_MEMORY)\n",
    "@md.env(pip_packages=[\"monai>=0.8.1\", \"torch>=1.10.2\", \"numpy>=1.21\", \"nibabel\"])\n",
    "class SpleenSegOperator(Operator):\n",
    "    \"\"\"Performs Spleen segmentation with a 3D image converted from a DICOM CT series.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "\n",
    "        self.logger = logging.getLogger(\"{}.{}\".format(__name__, type(self).__name__))\n",
    "        super().__init__()\n",
    "        self._input_dataset_key = \"image\"\n",
    "        self._pred_dataset_key = \"pred\"\n",
    "\n",
    "    def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):\n",
    "\n",
    "        input_image = op_input.get(\"image\")\n",
    "        if not input_image:\n",
    "            raise ValueError(\"Input image is not found.\")\n",
    "\n",
    "        output_path = context.output.get().path\n",
    "\n",
    "        # This operator gets an in-memory Image object, so a specialized ImageReader is needed.\n",
    "        _reader = InMemImageReader(input_image)\n",
    "        pre_transforms = self.pre_process(_reader)\n",
    "        post_transforms = self.post_process(pre_transforms, path.join(output_path, \"prediction_output\"))\n",
    "\n",
    "        # Delegates inference and saving output to the built-in operator.\n",
    "        infer_operator = MonaiSegInferenceOperator(\n",
    "            (\n",
    "                96,\n",
    "                96,\n",
    "                96,\n",
    "            ),\n",
    "            pre_transforms,\n",
    "            post_transforms,\n",
    "        )\n",
    "\n",
    "        # Setting the keys used in the dictironary based transforms may change.\n",
    "        infer_operator.input_dataset_key = self._input_dataset_key\n",
    "        infer_operator.pred_dataset_key = self._pred_dataset_key\n",
    "\n",
    "        # Now let the built-in operator handles the work with the I/O spec and execution context.\n",
    "        infer_operator.compute(op_input, op_output, context)\n",
    "\n",
    "    def pre_process(self, img_reader) -> Compose:\n",
    "        \"\"\"Composes transforms for preprocessing input before predicting on a model.\"\"\"\n",
    "\n",
    "        my_key = self._input_dataset_key\n",
    "        return Compose(\n",
    "            [\n",
    "                LoadImaged(keys=my_key, reader=img_reader),\n",
    "                EnsureChannelFirstd(keys=my_key),\n",
    "                Orientationd(keys=my_key, axcodes=\"RAS\"),\n",
    "                Spacingd(keys=my_key, pixdim=[1.5, 1.5, 2.9], mode=[\"bilinear\"]),\n",
    "                ScaleIntensityRanged(keys=my_key, a_min=-57, a_max=164, b_min=0.0, b_max=1.0, clip=True),\n",
    "                EnsureTyped(keys=my_key),\n",
    "            ]\n",
    "        )\n",
    "\n",
    "    def post_process(self, pre_transforms: Compose, out_dir: str = \"./prediction_output\") -> Compose:\n",
    "        \"\"\"Composes transforms for postprocessing the prediction results.\"\"\"\n",
    "\n",
    "        pred_key = self._pred_dataset_key\n",
    "        return Compose(\n",
    "            [\n",
    "                Activationsd(keys=pred_key, softmax=True),\n",
    "                Invertd(\n",
    "                    keys=pred_key,\n",
    "                    transform=pre_transforms,\n",
    "                    orig_keys=self._input_dataset_key,\n",
    "                    nearest_interp=False,\n",
    "                    to_tensor=True,\n",
    "                ),\n",
    "                AsDiscreted(keys=pred_key, argmax=True),\n",
    "                SaveImaged(\n",
    "                    keys=pred_key,\n",
    "                    output_dir=out_dir,\n",
    "                    output_postfix=\"seg\",\n",
    "                    output_dtype=uint8,\n",
    "                ),\n",
    "            ]\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating Application class\n",
    "\n",
    "Our application class would look like below.\n",
    "\n",
    "It defines `App` class, inheriting [Application](/modules/_autosummary/monai.deploy.core.Application) class.\n",
    "\n",
    "The requirements (resource and package dependency) for the App can be specified by using [@resource](/modules/_autosummary/monai.deploy.core.resource) and [@env](/modules/_autosummary/monai.deploy.core.env) decorators.\n",
    "\n",
    "The base class method, `compose`, is overridden. Objects required for DICOM parsing, series selection (selecting the first series for the current release), pixel data conversion to volume image, and segmentation instance creation are created, so is the model-specific `SpleenSegOperator`. The execution pipeline, as a Directed Acyclic Graph, is created by connecting these objects through <a href=\"../../modules/_autosummary/monai.deploy.core.Application.html#monai.deploy.core.Application.add_flow\">self.add_flow()</a>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "@resource(cpu=1, gpu=1, memory=\"7Gi\")\n",
    "class AISpleenSegApp(Application):\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        \"\"\"Creates an application instance.\"\"\"\n",
    "\n",
    "        self._logger = logging.getLogger(\"{}.{}\".format(__name__, type(self).__name__))\n",
    "        super().__init__(*args, **kwargs)\n",
    "\n",
    "    def run(self, *args, **kwargs):\n",
    "        # This method calls the base class to run. Can be omitted if simply calling through.\n",
    "        self._logger.debug(f\"Begin {self.run.__name__}\")\n",
    "        super().run(*args, **kwargs)\n",
    "        self._logger.debug(f\"End {self.run.__name__}\")\n",
    "\n",
    "    def compose(self):\n",
    "        \"\"\"Creates the app specific operators and chain them up in the processing DAG.\"\"\"\n",
    "\n",
    "        self._logger.debug(f\"Begin {self.compose.__name__}\")\n",
    "        # Creates the custom operator(s) as well as SDK built-in operator(s).\n",
    "        study_loader_op = DICOMDataLoaderOperator()\n",
    "        series_selector_op = DICOMSeriesSelectorOperator(rules=Sample_Rules_Text)\n",
    "        series_to_vol_op = DICOMSeriesToVolumeOperator()\n",
    "        # Model specific inference operator, supporting MONAI transforms.\n",
    "\n",
    "        # Creates the model specific segmentation operator\n",
    "        spleen_seg_op = SpleenSegOperator()\n",
    "\n",
    "        # Create DICOM Seg writer providing the required segment description for each segment with\n",
    "        # the actual algorithm and the pertinent organ/tissue.\n",
    "        # The segment_label, algorithm_name, and algorithm_version are limited to 64 chars.\n",
    "        # https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html\n",
    "        # User can Look up SNOMED CT codes at, e.g.\n",
    "        # https://bioportal.bioontology.org/ontologies/SNOMEDCT\n",
    "\n",
    "        _algorithm_name = \"3D segmentation of the Spleen from a CT series\"\n",
    "        _algorithm_family = codes.DCM.ArtificialIntelligence\n",
    "        _algorithm_version = \"0.1.0\"\n",
    "\n",
    "        segment_descriptions = [\n",
    "            SegmentDescription(\n",
    "                segment_label=\"Lung\",\n",
    "                segmented_property_category=codes.SCT.Organ,\n",
    "                segmented_property_type=codes.SCT.Lung,\n",
    "                algorithm_name=_algorithm_name,\n",
    "                algorithm_family=_algorithm_family,\n",
    "                algorithm_version=_algorithm_version,\n",
    "            ),\n",
    "        ]\n",
    "\n",
    "        custom_tags = {\"SeriesDescription\": \"AI generated Seg, not for clinical use.\"}\n",
    "\n",
    "        dicom_seg_writer = DICOMSegmentationWriterOperator(\n",
    "            segment_descriptions=segment_descriptions, custom_tags=custom_tags\n",
    "        )\n",
    "\n",
    "        # Create the processing pipeline, by specifying the source and destination operators, and\n",
    "        # ensuring the output from the former matches the input of the latter, in both name and type.\n",
    "        self.add_flow(study_loader_op, series_selector_op, {\"dicom_study_list\": \"dicom_study_list\"})\n",
    "        self.add_flow(\n",
    "            series_selector_op, series_to_vol_op, {\"study_selected_series_list\": \"study_selected_series_list\"}\n",
    "        )\n",
    "        self.add_flow(series_to_vol_op, spleen_seg_op, {\"image\": \"image\"})\n",
    "\n",
    "        # Note below the dicom_seg_writer requires two inputs, each coming from a source operator.\n",
    "        self.add_flow(\n",
    "            series_selector_op, dicom_seg_writer, {\"study_selected_series_list\": \"study_selected_series_list\"}\n",
    "        )\n",
    "        self.add_flow(spleen_seg_op, dicom_seg_writer, {\"seg_image\": \"seg_image\"})\n",
    "\n",
    "        self._logger.debug(f\"End {self.compose.__name__}\")\n",
    "\n",
    "\n",
    "# This is a sample series selection rule in JSON, simply selecting CT series.\n",
    "# If the study has more than 1 CT series, then all of them will be selected.\n",
    "# Please see more detail in DICOMSeriesSelectorOperator.\n",
    "# For list of string values, e.g. \"ImageType\": [\"PRIMARY\", \"ORIGINAL\"], it is a match if all elements\n",
    "# are all in the multi-value attribute of the DICOM series.\n",
    "\n",
    "Sample_Rules_Text = \"\"\"\n",
    "{\n",
    "    \"selections\": [\n",
    "        {\n",
    "            \"name\": \"CT Series\",\n",
    "            \"conditions\": {\n",
    "                \"StudyDescription\": \"(.*?)\",\n",
    "                \"Modality\": \"(?i)CT\",\n",
    "                \"SeriesDescription\": \"(.*?)\",\n",
    "                \"ImageType\": [\"PRIMARY\", \"ORIGINAL\"]\n",
    "            }\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "\"\"\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Executing app locally\n",
    "\n",
    "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.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mGoing to initiate execution of operator DICOMDataLoaderOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMDataLoaderOperator \u001b[33m(Process ID: 1073180, Operator ID: d3dedce7-cf25-425f-ba2f-a70665103680)\u001b[39m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[2022-10-18 21:27:52,341] [INFO] (root) - Finding series for Selection named: CT Series\n",
      "[2022-10-18 21:27:52,342] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291\n",
      "  # of series: 1\n",
      "[2022-10-18 21:27:52,343] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "[2022-10-18 21:27:52,344] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'\n",
      "[2022-10-18 21:27:52,345] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST\n",
      "[2022-10-18 21:27:52,346] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:27:52,347] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'\n",
      "[2022-10-18 21:27:52,348] [INFO] (root) -     Series attribute Modality value: CT\n",
      "[2022-10-18 21:27:52,348] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:27:52,349] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'\n",
      "[2022-10-18 21:27:52,350] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f\n",
      "[2022-10-18 21:27:52,350] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:27:52,351] [INFO] (root) - On attribute: 'ImageType' to match value: '['PRIMARY', 'ORIGINAL']'\n",
      "[2022-10-18 21:27:52,352] [INFO] (root) -     Series attribute ImageType value: None\n",
      "[2022-10-18 21:27:52,353] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mDone performing execution of operator DICOMDataLoaderOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesSelectorOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesSelectorOperator \u001b[33m(Process ID: 1073180, Operator ID: 1b41cb58-49c4-43ef-90fa-310769a4c261)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesSelectorOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesToVolumeOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesToVolumeOperator \u001b[33m(Process ID: 1073180, Operator ID: b4ac94fa-d5ee-482c-b8f6-f789b388b8c3)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesToVolumeOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator SpleenSegOperator\u001b[39m\n",
      "\u001b[32mExecuting operator SpleenSegOperator \u001b[33m(Process ID: 1073180, Operator ID: d4752eb4-0315-4add-bf80-1b9e50027ca6)\u001b[39m\n",
      "Converted Image object metadata:\n",
      "SeriesInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239, type <class 'str'>\n",
      "SeriesDate: 20090831, type <class 'str'>\n",
      "SeriesTime: 101721.452, type <class 'str'>\n",
      "Modality: CT, type <class 'str'>\n",
      "SeriesDescription: ABD/PANC 3.0 B31f, type <class 'str'>\n",
      "PatientPosition: HFS, type <class 'str'>\n",
      "SeriesNumber: 8, type <class 'int'>\n",
      "row_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "col_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "depth_pixel_spacing: 1.5, type <class 'float'>\n",
      "row_direction_cosine: [1.0, 0.0, 0.0], type <class 'list'>\n",
      "col_direction_cosine: [0.0, 1.0, 0.0], type <class 'list'>\n",
      "depth_direction_cosine: [0.0, 0.0, 1.0], type <class 'list'>\n",
      "dicom_affine_transform: [[   0.7890625    0.           0.        -197.60547  ]\n",
      " [   0.           0.7890625    0.        -398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "nifti_affine_transform: [[  -0.7890625   -0.          -0.         197.60547  ]\n",
      " [  -0.          -0.7890625   -0.         398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "StudyInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291, type <class 'str'>\n",
      "StudyID: , type <class 'str'>\n",
      "StudyDate: 20090831, type <class 'str'>\n",
      "StudyTime: 095948.599, type <class 'str'>\n",
      "StudyDescription: CT ABDOMEN W IV CONTRAST, type <class 'str'>\n",
      "AccessionNumber: 5471978513296937, type <class 'str'>\n",
      "selection_name: CT Series, type <class 'str'>\n",
      "2022-10-18 21:28:06,172 INFO image_writer.py:194 - writing: /home/mqin/src/monai-deploy-app-sdk/notebooks/tutorials/output/prediction_output/1.3.6.1.4.1.14519.5.2.1.7085.2626/1.3.6.1.4.1.14519.5.2.1.7085.2626_seg.nii.gz\n",
      "Output Seg image numpy array shaped: (204, 512, 512)\n",
      "Output Seg image pixel max value: 1\n",
      "\u001b[34mDone performing execution of operator SpleenSegOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSegmentationWriterOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSegmentationWriterOperator \u001b[33m(Process ID: 1073180, Operator ID: 4dc3c84a-3f4d-4c07-a44b-dfab39c888a4)\u001b[39m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/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.\n",
      "  warnings.warn(\n",
      "[2022-10-18 21:28:09,730] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1\n",
      "[2022-10-18 21:28:09,733] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1\n",
      "[2022-10-18 21:28:09,734] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1\n",
      "[2022-10-18 21:28:09,736] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1\n",
      "[2022-10-18 21:28:09,737] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1\n",
      "[2022-10-18 21:28:09,739] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1\n",
      "[2022-10-18 21:28:09,740] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1\n",
      "[2022-10-18 21:28:09,742] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1\n",
      "[2022-10-18 21:28:09,743] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1\n",
      "[2022-10-18 21:28:09,744] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1\n",
      "[2022-10-18 21:28:09,746] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1\n",
      "[2022-10-18 21:28:09,748] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1\n",
      "[2022-10-18 21:28:09,750] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1\n",
      "[2022-10-18 21:28:09,752] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1\n",
      "[2022-10-18 21:28:09,754] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1\n",
      "[2022-10-18 21:28:09,755] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1\n",
      "[2022-10-18 21:28:09,757] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1\n",
      "[2022-10-18 21:28:09,758] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1\n",
      "[2022-10-18 21:28:09,759] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1\n",
      "[2022-10-18 21:28:09,761] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1\n",
      "[2022-10-18 21:28:09,762] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1\n",
      "[2022-10-18 21:28:09,764] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1\n",
      "[2022-10-18 21:28:09,765] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1\n",
      "[2022-10-18 21:28:09,767] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1\n",
      "[2022-10-18 21:28:09,769] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1\n",
      "[2022-10-18 21:28:09,770] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1\n",
      "[2022-10-18 21:28:09,772] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1\n",
      "[2022-10-18 21:28:09,773] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1\n",
      "[2022-10-18 21:28:09,775] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1\n",
      "[2022-10-18 21:28:09,777] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1\n",
      "[2022-10-18 21:28:09,779] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1\n",
      "[2022-10-18 21:28:09,781] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1\n",
      "[2022-10-18 21:28:09,782] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1\n",
      "[2022-10-18 21:28:09,784] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1\n",
      "[2022-10-18 21:28:09,786] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1\n",
      "[2022-10-18 21:28:09,788] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1\n",
      "[2022-10-18 21:28:09,790] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1\n",
      "[2022-10-18 21:28:09,792] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1\n",
      "[2022-10-18 21:28:09,795] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1\n",
      "[2022-10-18 21:28:09,796] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1\n",
      "[2022-10-18 21:28:09,798] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1\n",
      "[2022-10-18 21:28:09,799] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1\n",
      "[2022-10-18 21:28:09,801] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1\n",
      "[2022-10-18 21:28:09,802] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1\n",
      "[2022-10-18 21:28:09,804] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1\n",
      "[2022-10-18 21:28:09,805] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1\n",
      "[2022-10-18 21:28:09,809] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1\n",
      "[2022-10-18 21:28:09,813] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1\n",
      "[2022-10-18 21:28:09,820] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1\n",
      "[2022-10-18 21:28:09,826] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1\n",
      "[2022-10-18 21:28:09,829] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1\n",
      "[2022-10-18 21:28:09,832] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1\n",
      "[2022-10-18 21:28:09,834] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1\n",
      "[2022-10-18 21:28:09,837] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1\n",
      "[2022-10-18 21:28:09,840] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1\n",
      "[2022-10-18 21:28:09,842] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1\n",
      "[2022-10-18 21:28:09,845] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1\n",
      "[2022-10-18 21:28:09,847] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1\n",
      "[2022-10-18 21:28:09,850] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1\n",
      "[2022-10-18 21:28:09,852] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1\n",
      "[2022-10-18 21:28:09,854] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1\n",
      "[2022-10-18 21:28:09,855] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1\n",
      "[2022-10-18 21:28:09,857] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1\n",
      "[2022-10-18 21:28:09,859] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1\n",
      "[2022-10-18 21:28:09,860] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1\n",
      "[2022-10-18 21:28:09,864] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1\n",
      "[2022-10-18 21:28:09,869] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1\n",
      "[2022-10-18 21:28:09,873] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1\n",
      "[2022-10-18 21:28:09,876] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1\n",
      "[2022-10-18 21:28:09,880] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1\n",
      "[2022-10-18 21:28:09,883] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1\n",
      "[2022-10-18 21:28:09,885] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1\n",
      "[2022-10-18 21:28:09,888] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1\n",
      "[2022-10-18 21:28:09,891] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1\n",
      "[2022-10-18 21:28:09,895] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1\n",
      "[2022-10-18 21:28:09,898] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1\n",
      "[2022-10-18 21:28:09,902] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1\n",
      "[2022-10-18 21:28:09,905] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1\n",
      "[2022-10-18 21:28:09,909] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1\n",
      "[2022-10-18 21:28:09,913] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1\n",
      "[2022-10-18 21:28:09,916] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1\n",
      "[2022-10-18 21:28:09,918] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1\n",
      "[2022-10-18 21:28:09,920] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1\n",
      "[2022-10-18 21:28:09,923] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1\n",
      "[2022-10-18 21:28:09,925] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1\n",
      "[2022-10-18 21:28:09,927] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1\n",
      "[2022-10-18 21:28:09,928] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1\n",
      "[2022-10-18 21:28:09,930] [INFO] (highdicom.seg.sop) - add plane #87 for segment #1\n",
      "[2022-10-18 21:28:09,977] [INFO] (highdicom.base) - copy Image-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:09,978] [INFO] (highdicom.base) - copy attributes of module \"Specimen\"\n",
      "[2022-10-18 21:28:09,978] [INFO] (highdicom.base) - copy Patient-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:09,979] [INFO] (highdicom.base) - copy attributes of module \"Patient\"\n",
      "[2022-10-18 21:28:09,979] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Subject\"\n",
      "[2022-10-18 21:28:09,980] [INFO] (highdicom.base) - copy Study-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:09,980] [INFO] (highdicom.base) - copy attributes of module \"General Study\"\n",
      "[2022-10-18 21:28:09,981] [INFO] (highdicom.base) - copy attributes of module \"Patient Study\"\n",
      "[2022-10-18 21:28:09,981] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Study\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mDone performing execution of operator DICOMSegmentationWriterOperator\n",
      "\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "app = AISpleenSegApp()\n",
    "\n",
    "app.run(input=\"dcm\", output=\"output\", model=\"model.ts\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the application is verified inside Jupyter notebook, we can write the above Python code into Python files in an application folder.\n",
    "\n",
    "The application folder structure would look like below:\n",
    "\n",
    "```bash\n",
    "my_app\n",
    "├── __main__.py\n",
    "├── app.py\n",
    "└── spleen_seg_operator.py\n",
    "```\n",
    "\n",
    ":::{note}\n",
    "We can create a single application Python file (such as `spleen_app.py`) that includes the content of the files, instead of creating multiple files.\n",
    "You will see such an example in <a href=\"./02_mednist_app.html#executing-app-locally\">MedNist Classifier Tutorial</a>.\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create an application folder\n",
    "!mkdir -p my_app"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### spleen_seg_operator.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing my_app/spleen_seg_operator.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile my_app/spleen_seg_operator.py\n",
    "import logging\n",
    "from os import path\n",
    "\n",
    "from numpy import uint8\n",
    "\n",
    "import monai.deploy.core as md\n",
    "from monai.deploy.core import ExecutionContext, Image, InputContext, IOType, Operator, OutputContext\n",
    "from monai.deploy.operators.monai_seg_inference_operator import InMemImageReader, MonaiSegInferenceOperator\n",
    "from monai.transforms import (\n",
    "    Activationsd,\n",
    "    AsDiscreted,\n",
    "    Compose,\n",
    "    EnsureChannelFirstd,\n",
    "    EnsureTyped,\n",
    "    Invertd,\n",
    "    LoadImaged,\n",
    "    Orientationd,\n",
    "    SaveImaged,\n",
    "    ScaleIntensityRanged,\n",
    "    Spacingd,\n",
    ")\n",
    "\n",
    "\n",
    "@md.input(\"image\", Image, IOType.IN_MEMORY)\n",
    "@md.output(\"seg_image\", Image, IOType.IN_MEMORY)\n",
    "@md.env(pip_packages=[\"monai>=0.8.1\", \"torch>=1.10.2\", \"numpy>=1.21\", \"nibabel\"])\n",
    "class SpleenSegOperator(Operator):\n",
    "    \"\"\"Performs Spleen segmentation with a 3D image converted from a DICOM CT series.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "\n",
    "        self.logger = logging.getLogger(\"{}.{}\".format(__name__, type(self).__name__))\n",
    "        super().__init__()\n",
    "        self._input_dataset_key = \"image\"\n",
    "        self._pred_dataset_key = \"pred\"\n",
    "\n",
    "    def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):\n",
    "\n",
    "        input_image = op_input.get(\"image\")\n",
    "        if not input_image:\n",
    "            raise ValueError(\"Input image is not found.\")\n",
    "\n",
    "        output_path = context.output.get().path\n",
    "\n",
    "        # This operator gets an in-memory Image object, so a specialized ImageReader is needed.\n",
    "        _reader = InMemImageReader(input_image)\n",
    "        pre_transforms = self.pre_process(_reader)\n",
    "        post_transforms = self.post_process(pre_transforms, path.join(output_path, \"prediction_output\"))\n",
    "\n",
    "        # Delegates inference and saving output to the built-in operator.\n",
    "        infer_operator = MonaiSegInferenceOperator(\n",
    "            (\n",
    "                96,\n",
    "                96,\n",
    "                96,\n",
    "            ),\n",
    "            pre_transforms,\n",
    "            post_transforms,\n",
    "        )\n",
    "\n",
    "        # Setting the keys used in the dictironary based transforms may change.\n",
    "        infer_operator.input_dataset_key = self._input_dataset_key\n",
    "        infer_operator.pred_dataset_key = self._pred_dataset_key\n",
    "\n",
    "        # Now let the built-in operator handles the work with the I/O spec and execution context.\n",
    "        infer_operator.compute(op_input, op_output, context)\n",
    "\n",
    "    def pre_process(self, img_reader) -> Compose:\n",
    "        \"\"\"Composes transforms for preprocessing input before predicting on a model.\"\"\"\n",
    "\n",
    "        my_key = self._input_dataset_key\n",
    "        return Compose(\n",
    "            [\n",
    "                LoadImaged(keys=my_key, reader=img_reader),\n",
    "                EnsureChannelFirstd(keys=my_key),\n",
    "                Orientationd(keys=my_key, axcodes=\"RAS\"),\n",
    "                Spacingd(keys=my_key, pixdim=[1.5, 1.5, 2.9], mode=[\"bilinear\"]),\n",
    "                ScaleIntensityRanged(keys=my_key, a_min=-57, a_max=164, b_min=0.0, b_max=1.0, clip=True),\n",
    "                EnsureTyped(keys=my_key),\n",
    "            ]\n",
    "        )\n",
    "\n",
    "    def post_process(self, pre_transforms: Compose, out_dir: str = \"./prediction_output\") -> Compose:\n",
    "        \"\"\"Composes transforms for postprocessing the prediction results.\"\"\"\n",
    "\n",
    "        pred_key = self._pred_dataset_key\n",
    "        return Compose(\n",
    "            [\n",
    "                Activationsd(keys=pred_key, softmax=True),\n",
    "                Invertd(\n",
    "                    keys=pred_key,\n",
    "                    transform=pre_transforms,\n",
    "                    orig_keys=self._input_dataset_key,\n",
    "                    nearest_interp=False,\n",
    "                    to_tensor=True,\n",
    "                ),\n",
    "                AsDiscreted(keys=pred_key, argmax=True),\n",
    "                SaveImaged(\n",
    "                    keys=pred_key,\n",
    "                    output_dir=out_dir,\n",
    "                    output_postfix=\"seg\",\n",
    "                    output_dtype=uint8,\n",
    "                ),\n",
    "            ]\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### app.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing my_app/app.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile my_app/app.py\n",
    "import logging\n",
    "\n",
    "from spleen_seg_operator import SpleenSegOperator\n",
    "\n",
    "# Required for setting SegmentDescription attributes. Direct import as this is not part of App SDK package.\n",
    "from pydicom.sr.codedict import codes\n",
    "\n",
    "from monai.deploy.core import Application, resource\n",
    "from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator\n",
    "from monai.deploy.operators.dicom_seg_writer_operator import DICOMSegmentationWriterOperator, SegmentDescription\n",
    "from monai.deploy.operators.dicom_series_selector_operator import DICOMSeriesSelectorOperator\n",
    "from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator\n",
    "\n",
    "# This is a sample series selection rule in JSON, simply selecting CT series.\n",
    "# If the study has more than 1 CT series, then all of them will be selected.\n",
    "# Please see more detail in DICOMSeriesSelectorOperator.\n",
    "Sample_Rules_Text = \"\"\"\n",
    "{\n",
    "    \"selections\": [\n",
    "        {\n",
    "            \"name\": \"CT Series\",\n",
    "            \"conditions\": {\n",
    "                \"StudyDescription\": \"(.*?)\",\n",
    "                \"Modality\": \"(?i)CT\",\n",
    "                \"SeriesDescription\": \"(.*?)\",\n",
    "                \"ImageType\": [\"PRIMARY\", \"ORIGINAL\"]\n",
    "            }\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "\"\"\"\n",
    "\n",
    "@resource(cpu=1, gpu=1, memory=\"7Gi\")\n",
    "class AISpleenSegApp(Application):\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        \"\"\"Creates an application instance.\"\"\"\n",
    "\n",
    "        self._logger = logging.getLogger(\"{}.{}\".format(__name__, type(self).__name__))\n",
    "        super().__init__(*args, **kwargs)\n",
    "\n",
    "    def run(self, *args, **kwargs):\n",
    "        # This method calls the base class to run. Can be omitted if simply calling through.\n",
    "        self._logger.debug(f\"Begin {self.run.__name__}\")\n",
    "        super().run(*args, **kwargs)\n",
    "        self._logger.debug(f\"End {self.run.__name__}\")\n",
    "\n",
    "    def compose(self):\n",
    "        \"\"\"Creates the app specific operators and chain them up in the processing DAG.\"\"\"\n",
    "\n",
    "        self._logger.debug(f\"Begin {self.compose.__name__}\")\n",
    "        # Creates the custom operator(s) as well as SDK built-in operator(s).\n",
    "        study_loader_op = DICOMDataLoaderOperator()\n",
    "        series_selector_op = DICOMSeriesSelectorOperator(rules=Sample_Rules_Text)\n",
    "        series_to_vol_op = DICOMSeriesToVolumeOperator()\n",
    "        # Model specific inference operator, supporting MONAI transforms.\n",
    "\n",
    "        # Creates the model specific segmentation operator\n",
    "        spleen_seg_op = SpleenSegOperator()\n",
    "\n",
    "        # Create DICOM Seg writer providing the required segment description for each segment with\n",
    "        # the actual algorithm and the pertinent organ/tissue.\n",
    "        # The segment_label, algorithm_name, and algorithm_version are limited to 64 chars.\n",
    "        # https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html\n",
    "        # User can Look up SNOMED CT codes at, e.g.\n",
    "        # https://bioportal.bioontology.org/ontologies/SNOMEDCT\n",
    "\n",
    "        _algorithm_name = \"3D segmentation of the Spleen from a CT series\"\n",
    "        _algorithm_family = codes.DCM.ArtificialIntelligence\n",
    "        _algorithm_version = \"0.1.0\"\n",
    "\n",
    "        segment_descriptions = [\n",
    "            SegmentDescription(\n",
    "                segment_label=\"Lung\",\n",
    "                segmented_property_category=codes.SCT.Organ,\n",
    "                segmented_property_type=codes.SCT.Lung,\n",
    "                algorithm_name=_algorithm_name,\n",
    "                algorithm_family=_algorithm_family,\n",
    "                algorithm_version=_algorithm_version,\n",
    "            ),\n",
    "        ]\n",
    "\n",
    "        custom_tags = {\"SeriesDescription\": \"AI generated Seg, not for clinical use.\"}\n",
    "\n",
    "        dicom_seg_writer = DICOMSegmentationWriterOperator(\n",
    "            segment_descriptions=segment_descriptions, custom_tags=custom_tags\n",
    "        )\n",
    "\n",
    "        # Create the processing pipeline, by specifying the source and destination operators, and\n",
    "        # ensuring the output from the former matches the input of the latter, in both name and type.\n",
    "        self.add_flow(study_loader_op, series_selector_op, {\"dicom_study_list\": \"dicom_study_list\"})\n",
    "        self.add_flow(\n",
    "            series_selector_op, series_to_vol_op, {\"study_selected_series_list\": \"study_selected_series_list\"}\n",
    "        )\n",
    "        self.add_flow(series_to_vol_op, spleen_seg_op, {\"image\": \"image\"})\n",
    "\n",
    "        # Note below the dicom_seg_writer requires two inputs, each coming from a source operator.\n",
    "        self.add_flow(\n",
    "            series_selector_op, dicom_seg_writer, {\"study_selected_series_list\": \"study_selected_series_list\"}\n",
    "        )\n",
    "        self.add_flow(spleen_seg_op, dicom_seg_writer, {\"seg_image\": \"seg_image\"})\n",
    "\n",
    "        self._logger.debug(f\"End {self.compose.__name__}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # Creates the app and test it standalone. When running is this mode, please note the following:\n",
    "    #     -i <DICOM folder>, for input DICOM CT series folder\n",
    "    #     -o <output folder>, for the output folder, default $PWD/output\n",
    "    #     -m <model file>, for model file path\n",
    "    # e.g.\n",
    "    #     python3 app.py -i input -m model.ts\n",
    "    #\n",
    "    AISpleenSegApp(do_run=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "if __name__ == \"__main__\":\n",
    "    AISpleenSegApp(do_run=True)\n",
    "```\n",
    "\n",
    "The above lines are needed to execute the application code by using `python` interpreter.\n",
    "\n",
    "### \\_\\_main\\_\\_.py\n",
    "\n",
    "\\_\\_main\\_\\_.py is needed for <a href=\"../../developing_with_sdk/packaging_app.html#required-arguments\">MONAI Application Packager</a> to detect the main application code (`app.py`) when the application is executed with the application folder path (e.g., `python simple_imaging_app`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing my_app/__main__.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile my_app/__main__.py\n",
    "from app import AISpleenSegApp\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    AISpleenSegApp(do_run=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "app.py\t__main__.py  spleen_seg_operator.py\n"
     ]
    }
   ],
   "source": [
    "!ls my_app"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this time, let's execute the app in the command line."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mGoing to initiate execution of operator DICOMDataLoaderOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMDataLoaderOperator \u001b[33m(Process ID: 1073440, Operator ID: 80e8d7c8-9198-443e-84a3-8055a8d7b83a)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMDataLoaderOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesSelectorOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesSelectorOperator \u001b[33m(Process ID: 1073440, Operator ID: bc4dd0d0-71d5-4c45-b254-1466cfd1f7a7)\u001b[39m\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - Finding series for Selection named: CT Series\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291\n",
      "  # of series: 1\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) -     Series attribute Modality value: CT\n",
      "[2022-10-18 21:28:15,990] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:28:15,991] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'\n",
      "[2022-10-18 21:28:15,991] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f\n",
      "[2022-10-18 21:28:15,991] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:28:15,991] [INFO] (root) - On attribute: 'ImageType' to match value: '['PRIMARY', 'ORIGINAL']'\n",
      "[2022-10-18 21:28:15,991] [INFO] (root) -     Series attribute ImageType value: None\n",
      "[2022-10-18 21:28:15,991] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesSelectorOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesToVolumeOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesToVolumeOperator \u001b[33m(Process ID: 1073440, Operator ID: 11f938ee-6c1f-484a-8e28-f34f575b17e0)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesToVolumeOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator SpleenSegOperator\u001b[39m\n",
      "\u001b[32mExecuting operator SpleenSegOperator \u001b[33m(Process ID: 1073440, Operator ID: ea4565db-e057-40ba-8708-e6c81a6eb7e5)\u001b[39m\n",
      "Converted Image object metadata:\n",
      "SeriesInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239, type <class 'str'>\n",
      "SeriesDate: 20090831, type <class 'str'>\n",
      "SeriesTime: 101721.452, type <class 'str'>\n",
      "Modality: CT, type <class 'str'>\n",
      "SeriesDescription: ABD/PANC 3.0 B31f, type <class 'str'>\n",
      "PatientPosition: HFS, type <class 'str'>\n",
      "SeriesNumber: 8, type <class 'int'>\n",
      "row_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "col_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "depth_pixel_spacing: 1.5, type <class 'float'>\n",
      "row_direction_cosine: [1.0, 0.0, 0.0], type <class 'list'>\n",
      "col_direction_cosine: [0.0, 1.0, 0.0], type <class 'list'>\n",
      "depth_direction_cosine: [0.0, 0.0, 1.0], type <class 'list'>\n",
      "dicom_affine_transform: [[   0.7890625    0.           0.        -197.60547  ]\n",
      " [   0.           0.7890625    0.        -398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "nifti_affine_transform: [[  -0.7890625   -0.          -0.         197.60547  ]\n",
      " [  -0.          -0.7890625   -0.         398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "StudyInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291, type <class 'str'>\n",
      "StudyID: , type <class 'str'>\n",
      "StudyDate: 20090831, type <class 'str'>\n",
      "StudyTime: 095948.599, type <class 'str'>\n",
      "StudyDescription: CT ABDOMEN W IV CONTRAST, type <class 'str'>\n",
      "AccessionNumber: 5471978513296937, type <class 'str'>\n",
      "selection_name: CT Series, type <class 'str'>\n",
      "2022-10-18 21:28:29,842 INFO image_writer.py:194 - writing: /home/mqin/src/monai-deploy-app-sdk/notebooks/tutorials/output/prediction_output/1.3.6.1.4.1.14519.5.2.1.7085.2626/1.3.6.1.4.1.14519.5.2.1.7085.2626_seg.nii.gz\n",
      "Output Seg image numpy array shaped: (204, 512, 512)\n",
      "Output Seg image pixel max value: 1\n",
      "\u001b[34mDone performing execution of operator SpleenSegOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSegmentationWriterOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSegmentationWriterOperator \u001b[33m(Process ID: 1073440, Operator ID: 2fcc7247-e95b-448a-a1e6-787d262cfa1d)\u001b[39m\n",
      "/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.\n",
      "  warnings.warn(\n",
      "[2022-10-18 21:28:33,270] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1\n",
      "[2022-10-18 21:28:33,271] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1\n",
      "[2022-10-18 21:28:33,272] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1\n",
      "[2022-10-18 21:28:33,273] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1\n",
      "[2022-10-18 21:28:33,274] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1\n",
      "[2022-10-18 21:28:33,275] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1\n",
      "[2022-10-18 21:28:33,276] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1\n",
      "[2022-10-18 21:28:33,277] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1\n",
      "[2022-10-18 21:28:33,278] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1\n",
      "[2022-10-18 21:28:33,279] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1\n",
      "[2022-10-18 21:28:33,280] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1\n",
      "[2022-10-18 21:28:33,281] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1\n",
      "[2022-10-18 21:28:33,282] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1\n",
      "[2022-10-18 21:28:33,283] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1\n",
      "[2022-10-18 21:28:33,285] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1\n",
      "[2022-10-18 21:28:33,286] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1\n",
      "[2022-10-18 21:28:33,287] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1\n",
      "[2022-10-18 21:28:33,288] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1\n",
      "[2022-10-18 21:28:33,289] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1\n",
      "[2022-10-18 21:28:33,290] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1\n",
      "[2022-10-18 21:28:33,291] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1\n",
      "[2022-10-18 21:28:33,292] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1\n",
      "[2022-10-18 21:28:33,293] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1\n",
      "[2022-10-18 21:28:33,294] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1\n",
      "[2022-10-18 21:28:33,295] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1\n",
      "[2022-10-18 21:28:33,297] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1\n",
      "[2022-10-18 21:28:33,298] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1\n",
      "[2022-10-18 21:28:33,299] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1\n",
      "[2022-10-18 21:28:33,300] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1\n",
      "[2022-10-18 21:28:33,301] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1\n",
      "[2022-10-18 21:28:33,302] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1\n",
      "[2022-10-18 21:28:33,303] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1\n",
      "[2022-10-18 21:28:33,303] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1\n",
      "[2022-10-18 21:28:33,304] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1\n",
      "[2022-10-18 21:28:33,305] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1\n",
      "[2022-10-18 21:28:33,306] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1\n",
      "[2022-10-18 21:28:33,307] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1\n",
      "[2022-10-18 21:28:33,308] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1\n",
      "[2022-10-18 21:28:33,309] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1\n",
      "[2022-10-18 21:28:33,310] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1\n",
      "[2022-10-18 21:28:33,311] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1\n",
      "[2022-10-18 21:28:33,312] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1\n",
      "[2022-10-18 21:28:33,313] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1\n",
      "[2022-10-18 21:28:33,314] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1\n",
      "[2022-10-18 21:28:33,315] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1\n",
      "[2022-10-18 21:28:33,316] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1\n",
      "[2022-10-18 21:28:33,317] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1\n",
      "[2022-10-18 21:28:33,318] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1\n",
      "[2022-10-18 21:28:33,319] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1\n",
      "[2022-10-18 21:28:33,320] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1\n",
      "[2022-10-18 21:28:33,321] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1\n",
      "[2022-10-18 21:28:33,322] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1\n",
      "[2022-10-18 21:28:33,323] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1\n",
      "[2022-10-18 21:28:33,324] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1\n",
      "[2022-10-18 21:28:33,325] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1\n",
      "[2022-10-18 21:28:33,326] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1\n",
      "[2022-10-18 21:28:33,327] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1\n",
      "[2022-10-18 21:28:33,328] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1\n",
      "[2022-10-18 21:28:33,329] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1\n",
      "[2022-10-18 21:28:33,330] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1\n",
      "[2022-10-18 21:28:33,331] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1\n",
      "[2022-10-18 21:28:33,332] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1\n",
      "[2022-10-18 21:28:33,333] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1\n",
      "[2022-10-18 21:28:33,334] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1\n",
      "[2022-10-18 21:28:33,335] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1\n",
      "[2022-10-18 21:28:33,336] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1\n",
      "[2022-10-18 21:28:33,337] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1\n",
      "[2022-10-18 21:28:33,338] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1\n",
      "[2022-10-18 21:28:33,339] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1\n",
      "[2022-10-18 21:28:33,340] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1\n",
      "[2022-10-18 21:28:33,341] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1\n",
      "[2022-10-18 21:28:33,342] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1\n",
      "[2022-10-18 21:28:33,343] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1\n",
      "[2022-10-18 21:28:33,344] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1\n",
      "[2022-10-18 21:28:33,346] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1\n",
      "[2022-10-18 21:28:33,347] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1\n",
      "[2022-10-18 21:28:33,348] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1\n",
      "[2022-10-18 21:28:33,349] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1\n",
      "[2022-10-18 21:28:33,350] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1\n",
      "[2022-10-18 21:28:33,351] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1\n",
      "[2022-10-18 21:28:33,352] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1\n",
      "[2022-10-18 21:28:33,353] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1\n",
      "[2022-10-18 21:28:33,354] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1\n",
      "[2022-10-18 21:28:33,355] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1\n",
      "[2022-10-18 21:28:33,356] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1\n",
      "[2022-10-18 21:28:33,357] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1\n",
      "[2022-10-18 21:28:33,358] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1\n",
      "[2022-10-18 21:28:33,359] [INFO] (highdicom.seg.sop) - add plane #87 for segment #1\n",
      "[2022-10-18 21:28:33,404] [INFO] (highdicom.base) - copy Image-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:33,404] [INFO] (highdicom.base) - copy attributes of module \"Specimen\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy Patient-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy attributes of module \"Patient\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Subject\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy Study-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy attributes of module \"General Study\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy attributes of module \"Patient Study\"\n",
      "[2022-10-18 21:28:33,405] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Study\"\n",
      "\u001b[34mDone performing execution of operator DICOMSegmentationWriterOperator\n",
      "\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "!python my_app -i dcm -o output -m model.ts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Above command is same with the following command line:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mGoing to initiate execution of operator DICOMDataLoaderOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMDataLoaderOperator \u001b[33m(Process ID: 1073501, Operator ID: 66133a89-7330-4de5-bdd8-b39e46603565)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMDataLoaderOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesSelectorOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesSelectorOperator \u001b[33m(Process ID: 1073501, Operator ID: 56990475-971c-412d-bb1c-4d01d6681064)\u001b[39m\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - Finding series for Selection named: CT Series\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291\n",
      "  # of series: 1\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) -     Series attribute Modality value: CT\n",
      "[2022-10-18 21:28:38,695] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:28:38,696] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'\n",
      "[2022-10-18 21:28:38,696] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f\n",
      "[2022-10-18 21:28:38,696] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-18 21:28:38,696] [INFO] (root) - On attribute: 'ImageType' to match value: '['PRIMARY', 'ORIGINAL']'\n",
      "[2022-10-18 21:28:38,696] [INFO] (root) -     Series attribute ImageType value: None\n",
      "[2022-10-18 21:28:38,696] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesSelectorOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesToVolumeOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesToVolumeOperator \u001b[33m(Process ID: 1073501, Operator ID: c7f385c8-02c1-480c-8624-a7cbe635c6d1)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesToVolumeOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator SpleenSegOperator\u001b[39m\n",
      "\u001b[32mExecuting operator SpleenSegOperator \u001b[33m(Process ID: 1073501, Operator ID: 499282f6-c8ec-4b6e-a15f-8f8d12e1d202)\u001b[39m\n",
      "Converted Image object metadata:\n",
      "SeriesInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239, type <class 'str'>\n",
      "SeriesDate: 20090831, type <class 'str'>\n",
      "SeriesTime: 101721.452, type <class 'str'>\n",
      "Modality: CT, type <class 'str'>\n",
      "SeriesDescription: ABD/PANC 3.0 B31f, type <class 'str'>\n",
      "PatientPosition: HFS, type <class 'str'>\n",
      "SeriesNumber: 8, type <class 'int'>\n",
      "row_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "col_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "depth_pixel_spacing: 1.5, type <class 'float'>\n",
      "row_direction_cosine: [1.0, 0.0, 0.0], type <class 'list'>\n",
      "col_direction_cosine: [0.0, 1.0, 0.0], type <class 'list'>\n",
      "depth_direction_cosine: [0.0, 0.0, 1.0], type <class 'list'>\n",
      "dicom_affine_transform: [[   0.7890625    0.           0.        -197.60547  ]\n",
      " [   0.           0.7890625    0.        -398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "nifti_affine_transform: [[  -0.7890625   -0.          -0.         197.60547  ]\n",
      " [  -0.          -0.7890625   -0.         398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "StudyInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291, type <class 'str'>\n",
      "StudyID: , type <class 'str'>\n",
      "StudyDate: 20090831, type <class 'str'>\n",
      "StudyTime: 095948.599, type <class 'str'>\n",
      "StudyDescription: CT ABDOMEN W IV CONTRAST, type <class 'str'>\n",
      "AccessionNumber: 5471978513296937, type <class 'str'>\n",
      "selection_name: CT Series, type <class 'str'>\n",
      "2022-10-18 21:28:52,347 INFO image_writer.py:194 - writing: /home/mqin/src/monai-deploy-app-sdk/notebooks/tutorials/output/prediction_output/1.3.6.1.4.1.14519.5.2.1.7085.2626/1.3.6.1.4.1.14519.5.2.1.7085.2626_seg.nii.gz\n",
      "Output Seg image numpy array shaped: (204, 512, 512)\n",
      "Output Seg image pixel max value: 1\n",
      "\u001b[34mDone performing execution of operator SpleenSegOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSegmentationWriterOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSegmentationWriterOperator \u001b[33m(Process ID: 1073501, Operator ID: e8c56c3c-f244-41b0-9b7c-903d7b3b1a8d)\u001b[39m\n",
      "/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.\n",
      "  warnings.warn(\n",
      "[2022-10-18 21:28:55,727] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1\n",
      "[2022-10-18 21:28:55,728] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1\n",
      "[2022-10-18 21:28:55,729] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1\n",
      "[2022-10-18 21:28:55,730] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1\n",
      "[2022-10-18 21:28:55,731] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1\n",
      "[2022-10-18 21:28:55,732] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1\n",
      "[2022-10-18 21:28:55,733] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1\n",
      "[2022-10-18 21:28:55,734] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1\n",
      "[2022-10-18 21:28:55,735] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1\n",
      "[2022-10-18 21:28:55,736] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1\n",
      "[2022-10-18 21:28:55,737] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1\n",
      "[2022-10-18 21:28:55,738] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1\n",
      "[2022-10-18 21:28:55,739] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1\n",
      "[2022-10-18 21:28:55,740] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1\n",
      "[2022-10-18 21:28:55,741] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1\n",
      "[2022-10-18 21:28:55,742] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1\n",
      "[2022-10-18 21:28:55,743] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1\n",
      "[2022-10-18 21:28:55,744] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1\n",
      "[2022-10-18 21:28:55,745] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1\n",
      "[2022-10-18 21:28:55,746] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1\n",
      "[2022-10-18 21:28:55,747] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1\n",
      "[2022-10-18 21:28:55,748] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1\n",
      "[2022-10-18 21:28:55,749] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1\n",
      "[2022-10-18 21:28:55,750] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1\n",
      "[2022-10-18 21:28:55,751] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1\n",
      "[2022-10-18 21:28:55,752] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1\n",
      "[2022-10-18 21:28:55,753] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1\n",
      "[2022-10-18 21:28:55,754] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1\n",
      "[2022-10-18 21:28:55,755] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1\n",
      "[2022-10-18 21:28:55,756] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1\n",
      "[2022-10-18 21:28:55,757] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1\n",
      "[2022-10-18 21:28:55,758] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1\n",
      "[2022-10-18 21:28:55,759] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1\n",
      "[2022-10-18 21:28:55,760] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1\n",
      "[2022-10-18 21:28:55,761] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1\n",
      "[2022-10-18 21:28:55,762] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1\n",
      "[2022-10-18 21:28:55,763] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1\n",
      "[2022-10-18 21:28:55,764] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1\n",
      "[2022-10-18 21:28:55,765] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1\n",
      "[2022-10-18 21:28:55,766] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1\n",
      "[2022-10-18 21:28:55,767] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1\n",
      "[2022-10-18 21:28:55,767] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1\n",
      "[2022-10-18 21:28:55,768] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1\n",
      "[2022-10-18 21:28:55,769] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1\n",
      "[2022-10-18 21:28:55,770] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1\n",
      "[2022-10-18 21:28:55,771] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1\n",
      "[2022-10-18 21:28:55,772] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1\n",
      "[2022-10-18 21:28:55,773] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1\n",
      "[2022-10-18 21:28:55,774] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1\n",
      "[2022-10-18 21:28:55,775] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1\n",
      "[2022-10-18 21:28:55,776] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1\n",
      "[2022-10-18 21:28:55,777] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1\n",
      "[2022-10-18 21:28:55,778] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1\n",
      "[2022-10-18 21:28:55,779] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1\n",
      "[2022-10-18 21:28:55,780] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1\n",
      "[2022-10-18 21:28:55,781] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1\n",
      "[2022-10-18 21:28:55,782] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1\n",
      "[2022-10-18 21:28:55,783] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1\n",
      "[2022-10-18 21:28:55,784] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1\n",
      "[2022-10-18 21:28:55,785] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1\n",
      "[2022-10-18 21:28:55,786] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1\n",
      "[2022-10-18 21:28:55,787] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1\n",
      "[2022-10-18 21:28:55,788] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1\n",
      "[2022-10-18 21:28:55,789] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1\n",
      "[2022-10-18 21:28:55,790] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1\n",
      "[2022-10-18 21:28:55,791] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1\n",
      "[2022-10-18 21:28:55,792] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1\n",
      "[2022-10-18 21:28:55,794] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1\n",
      "[2022-10-18 21:28:55,795] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1\n",
      "[2022-10-18 21:28:55,796] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1\n",
      "[2022-10-18 21:28:55,797] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1\n",
      "[2022-10-18 21:28:55,798] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1\n",
      "[2022-10-18 21:28:55,799] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1\n",
      "[2022-10-18 21:28:55,800] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1\n",
      "[2022-10-18 21:28:55,801] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1\n",
      "[2022-10-18 21:28:55,802] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1\n",
      "[2022-10-18 21:28:55,803] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1\n",
      "[2022-10-18 21:28:55,804] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1\n",
      "[2022-10-18 21:28:55,805] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1\n",
      "[2022-10-18 21:28:55,806] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1\n",
      "[2022-10-18 21:28:55,807] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1\n",
      "[2022-10-18 21:28:55,808] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1\n",
      "[2022-10-18 21:28:55,809] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1\n",
      "[2022-10-18 21:28:55,810] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1\n",
      "[2022-10-18 21:28:55,811] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1\n",
      "[2022-10-18 21:28:55,812] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1\n",
      "[2022-10-18 21:28:55,814] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1\n",
      "[2022-10-18 21:28:55,815] [INFO] (highdicom.seg.sop) - add plane #87 for segment #1\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy Image-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy attributes of module \"Specimen\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy Patient-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy attributes of module \"Patient\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Subject\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy Study-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy attributes of module \"General Study\"\n",
      "[2022-10-18 21:28:55,861] [INFO] (highdicom.base) - copy attributes of module \"Patient Study\"\n",
      "[2022-10-18 21:28:55,862] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Study\"\n",
      "\u001b[34mDone performing execution of operator DICOMSegmentationWriterOperator\n",
      "\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "os.environ['MKL_THREADING_LAYER'] = 'GNU'\n",
    "!monai-deploy exec my_app -i dcm -o output -m model.ts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.2.826.0.1.3680043.10.511.3.11520264113032864635597744730480621.dcm\n",
      "1.2.826.0.1.3680043.10.511.3.60037419814661212759821378502636860.dcm\n",
      "1.2.826.0.1.3680043.10.511.3.62411810421842534426987578192611738.dcm\n",
      "prediction_output\n"
     ]
    }
   ],
   "source": [
    "!ls output"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Packaging app"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's package the app with [MONAI Application Packager](/developing_with_sdk/packaging_app)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building MONAI Application Package... Done\n",
      "[2022-10-18 21:31:49,815] [INFO] (app_packager) - Successfully built my_app:latest\n"
     ]
    }
   ],
   "source": [
    "!monai-deploy package -b nvcr.io/nvidia/pytorch:21.11-py3 my_app --tag my_app:latest -m model.ts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{note}\n",
    "Building a MONAI Application Package (Docker image) can take time. Use `-l DEBUG` option if you want to see the progress.\n",
    ":::\n",
    "\n",
    "We can see that the Docker image is created."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "my_app                                                                latest                                     cd8a77c9c781   4 seconds ago    15.1GB\n"
     ]
    }
   ],
   "source": [
    "!docker image ls | grep my_app"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Executing packaged app locally\n",
    "\n",
    "The packaged app can be run locally through [MONAI Application Runner](/developing_with_sdk/executing_packaged_app_locally)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Checking dependencies...\n",
      "--> Verifying if \"docker\" is installed...\n",
      "\n",
      "--> Verifying if \"my_app:latest\" is available...\n",
      "\n",
      "Checking for MAP \"my_app:latest\" locally\n",
      "\"my_app:latest\" found.\n",
      "\n",
      "Reading MONAI App Package manifest...\n",
      "--> Verifying if \"nvidia-docker\" is installed...\n",
      "\n",
      "/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)\n",
      "  warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion} is required for this version of \"\n",
      "\u001b[34mGoing to initiate execution of operator DICOMDataLoaderOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMDataLoaderOperator \u001b[33m(Process ID: 1, Operator ID: 8ed98656-f2d5-4d7b-8b17-86fe78ed66c7)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMDataLoaderOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesSelectorOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesSelectorOperator \u001b[33m(Process ID: 1, Operator ID: 6944859e-643f-4914-81d8-26b18ce05f71)\u001b[39m\n",
      "[2022-10-19 04:32:07,327] [INFO] (root) - Finding series for Selection named: CT Series\n",
      "[2022-10-19 04:32:07,327] [INFO] (root) - Searching study, : 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291\n",
      "  # of series: 1\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - Working on series, instance UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - On attribute: 'StudyDescription' to match value: '(.*?)'\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) -     Series attribute StudyDescription value: CT ABDOMEN W IV CONTRAST\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - On attribute: 'Modality' to match value: '(?i)CT'\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) -     Series attribute Modality value: CT\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - On attribute: 'SeriesDescription' to match value: '(.*?)'\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) -     Series attribute SeriesDescription value: ABD/PANC 3.0 B31f\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - Series attribute string value did not match. Try regEx.\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - On attribute: 'ImageType' to match value: '['PRIMARY', 'ORIGINAL']'\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) -     Series attribute ImageType value: None\n",
      "[2022-10-19 04:32:07,328] [INFO] (root) - Selected Series, UID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesSelectorOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSeriesToVolumeOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSeriesToVolumeOperator \u001b[33m(Process ID: 1, Operator ID: 8b4c3c73-c2f8-4460-a256-2ddd8e58964c)\u001b[39m\n",
      "\u001b[34mDone performing execution of operator DICOMSeriesToVolumeOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator SpleenSegOperator\u001b[39m\n",
      "\u001b[32mExecuting operator SpleenSegOperator \u001b[33m(Process ID: 1, Operator ID: 510d5b42-d2d8-453a-806b-55d60ea0f106)\u001b[39m\n",
      "Converted Image object metadata:\n",
      "SeriesInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.119403521930927333027265674239, type <class 'str'>\n",
      "SeriesDate: 20090831, type <class 'str'>\n",
      "SeriesTime: 101721.452, type <class 'str'>\n",
      "Modality: CT, type <class 'str'>\n",
      "SeriesDescription: ABD/PANC 3.0 B31f, type <class 'str'>\n",
      "PatientPosition: HFS, type <class 'str'>\n",
      "SeriesNumber: 8, type <class 'int'>\n",
      "row_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "col_pixel_spacing: 0.7890625, type <class 'float'>\n",
      "depth_pixel_spacing: 1.5, type <class 'float'>\n",
      "row_direction_cosine: [1.0, 0.0, 0.0], type <class 'list'>\n",
      "col_direction_cosine: [0.0, 1.0, 0.0], type <class 'list'>\n",
      "depth_direction_cosine: [0.0, 0.0, 1.0], type <class 'list'>\n",
      "dicom_affine_transform: [[   0.7890625    0.           0.        -197.60547  ]\n",
      " [   0.           0.7890625    0.        -398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "nifti_affine_transform: [[  -0.7890625   -0.          -0.         197.60547  ]\n",
      " [  -0.          -0.7890625   -0.         398.60547  ]\n",
      " [   0.           0.           1.5       -383.       ]\n",
      " [   0.           0.           0.           1.       ]], type <class 'numpy.ndarray'>\n",
      "StudyInstanceUID: 1.3.6.1.4.1.14519.5.2.1.7085.2626.822645453932810382886582736291, type <class 'str'>\n",
      "StudyID: , type <class 'str'>\n",
      "StudyDate: 20090831, type <class 'str'>\n",
      "StudyTime: 095948.599, type <class 'str'>\n",
      "StudyDescription: CT ABDOMEN W IV CONTRAST, type <class 'str'>\n",
      "AccessionNumber: 5471978513296937, type <class 'str'>\n",
      "selection_name: CT Series, type <class 'str'>\n",
      "2022-10-19 04:32:23,910 INFO image_writer.py:194 - writing: /var/monai/output/prediction_output/1.3.6.1.4.1.14519.5.2.1.7085.2626/1.3.6.1.4.1.14519.5.2.1.7085.2626_seg.nii.gz\n",
      "Output Seg image numpy array shaped: (204, 512, 512)\n",
      "Output Seg image pixel max value: 1\n",
      "\u001b[34mDone performing execution of operator SpleenSegOperator\n",
      "\u001b[39m\n",
      "\u001b[34mGoing to initiate execution of operator DICOMSegmentationWriterOperator\u001b[39m\n",
      "\u001b[32mExecuting operator DICOMSegmentationWriterOperator \u001b[33m(Process ID: 1, Operator ID: 7774b902-43ca-4266-8ca6-bf022267707a)\u001b[39m\n",
      "/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.\n",
      "  warnings.warn(\n",
      "[2022-10-19 04:32:27,247] [INFO] (highdicom.seg.sop) - add plane #0 for segment #1\n",
      "[2022-10-19 04:32:27,249] [INFO] (highdicom.seg.sop) - add plane #1 for segment #1\n",
      "[2022-10-19 04:32:27,250] [INFO] (highdicom.seg.sop) - add plane #2 for segment #1\n",
      "[2022-10-19 04:32:27,251] [INFO] (highdicom.seg.sop) - add plane #3 for segment #1\n",
      "[2022-10-19 04:32:27,253] [INFO] (highdicom.seg.sop) - add plane #4 for segment #1\n",
      "[2022-10-19 04:32:27,254] [INFO] (highdicom.seg.sop) - add plane #5 for segment #1\n",
      "[2022-10-19 04:32:27,255] [INFO] (highdicom.seg.sop) - add plane #6 for segment #1\n",
      "[2022-10-19 04:32:27,257] [INFO] (highdicom.seg.sop) - add plane #7 for segment #1\n",
      "[2022-10-19 04:32:27,258] [INFO] (highdicom.seg.sop) - add plane #8 for segment #1\n",
      "[2022-10-19 04:32:27,260] [INFO] (highdicom.seg.sop) - add plane #9 for segment #1\n",
      "[2022-10-19 04:32:27,261] [INFO] (highdicom.seg.sop) - add plane #10 for segment #1\n",
      "[2022-10-19 04:32:27,263] [INFO] (highdicom.seg.sop) - add plane #11 for segment #1\n",
      "[2022-10-19 04:32:27,264] [INFO] (highdicom.seg.sop) - add plane #12 for segment #1\n",
      "[2022-10-19 04:32:27,265] [INFO] (highdicom.seg.sop) - add plane #13 for segment #1\n",
      "[2022-10-19 04:32:27,267] [INFO] (highdicom.seg.sop) - add plane #14 for segment #1\n",
      "[2022-10-19 04:32:27,268] [INFO] (highdicom.seg.sop) - add plane #15 for segment #1\n",
      "[2022-10-19 04:32:27,269] [INFO] (highdicom.seg.sop) - add plane #16 for segment #1\n",
      "[2022-10-19 04:32:27,271] [INFO] (highdicom.seg.sop) - add plane #17 for segment #1\n",
      "[2022-10-19 04:32:27,272] [INFO] (highdicom.seg.sop) - add plane #18 for segment #1\n",
      "[2022-10-19 04:32:27,274] [INFO] (highdicom.seg.sop) - add plane #19 for segment #1\n",
      "[2022-10-19 04:32:27,275] [INFO] (highdicom.seg.sop) - add plane #20 for segment #1\n",
      "[2022-10-19 04:32:27,277] [INFO] (highdicom.seg.sop) - add plane #21 for segment #1\n",
      "[2022-10-19 04:32:27,278] [INFO] (highdicom.seg.sop) - add plane #22 for segment #1\n",
      "[2022-10-19 04:32:27,280] [INFO] (highdicom.seg.sop) - add plane #23 for segment #1\n",
      "[2022-10-19 04:32:27,281] [INFO] (highdicom.seg.sop) - add plane #24 for segment #1\n",
      "[2022-10-19 04:32:27,282] [INFO] (highdicom.seg.sop) - add plane #25 for segment #1\n",
      "[2022-10-19 04:32:27,284] [INFO] (highdicom.seg.sop) - add plane #26 for segment #1\n",
      "[2022-10-19 04:32:27,285] [INFO] (highdicom.seg.sop) - add plane #27 for segment #1\n",
      "[2022-10-19 04:32:27,287] [INFO] (highdicom.seg.sop) - add plane #28 for segment #1\n",
      "[2022-10-19 04:32:27,288] [INFO] (highdicom.seg.sop) - add plane #29 for segment #1\n",
      "[2022-10-19 04:32:27,289] [INFO] (highdicom.seg.sop) - add plane #30 for segment #1\n",
      "[2022-10-19 04:32:27,291] [INFO] (highdicom.seg.sop) - add plane #31 for segment #1\n",
      "[2022-10-19 04:32:27,293] [INFO] (highdicom.seg.sop) - add plane #32 for segment #1\n",
      "[2022-10-19 04:32:27,294] [INFO] (highdicom.seg.sop) - add plane #33 for segment #1\n",
      "[2022-10-19 04:32:27,296] [INFO] (highdicom.seg.sop) - add plane #34 for segment #1\n",
      "[2022-10-19 04:32:27,297] [INFO] (highdicom.seg.sop) - add plane #35 for segment #1\n",
      "[2022-10-19 04:32:27,298] [INFO] (highdicom.seg.sop) - add plane #36 for segment #1\n",
      "[2022-10-19 04:32:27,300] [INFO] (highdicom.seg.sop) - add plane #37 for segment #1\n",
      "[2022-10-19 04:32:27,301] [INFO] (highdicom.seg.sop) - add plane #38 for segment #1\n",
      "[2022-10-19 04:32:27,303] [INFO] (highdicom.seg.sop) - add plane #39 for segment #1\n",
      "[2022-10-19 04:32:27,304] [INFO] (highdicom.seg.sop) - add plane #40 for segment #1\n",
      "[2022-10-19 04:32:27,306] [INFO] (highdicom.seg.sop) - add plane #41 for segment #1\n",
      "[2022-10-19 04:32:27,307] [INFO] (highdicom.seg.sop) - add plane #42 for segment #1\n",
      "[2022-10-19 04:32:27,309] [INFO] (highdicom.seg.sop) - add plane #43 for segment #1\n",
      "[2022-10-19 04:32:27,310] [INFO] (highdicom.seg.sop) - add plane #44 for segment #1\n",
      "[2022-10-19 04:32:27,312] [INFO] (highdicom.seg.sop) - add plane #45 for segment #1\n",
      "[2022-10-19 04:32:27,313] [INFO] (highdicom.seg.sop) - add plane #46 for segment #1\n",
      "[2022-10-19 04:32:27,315] [INFO] (highdicom.seg.sop) - add plane #47 for segment #1\n",
      "[2022-10-19 04:32:27,316] [INFO] (highdicom.seg.sop) - add plane #48 for segment #1\n",
      "[2022-10-19 04:32:27,318] [INFO] (highdicom.seg.sop) - add plane #49 for segment #1\n",
      "[2022-10-19 04:32:27,319] [INFO] (highdicom.seg.sop) - add plane #50 for segment #1\n",
      "[2022-10-19 04:32:27,321] [INFO] (highdicom.seg.sop) - add plane #51 for segment #1\n",
      "[2022-10-19 04:32:27,322] [INFO] (highdicom.seg.sop) - add plane #52 for segment #1\n",
      "[2022-10-19 04:32:27,324] [INFO] (highdicom.seg.sop) - add plane #53 for segment #1\n",
      "[2022-10-19 04:32:27,325] [INFO] (highdicom.seg.sop) - add plane #54 for segment #1\n",
      "[2022-10-19 04:32:27,327] [INFO] (highdicom.seg.sop) - add plane #55 for segment #1\n",
      "[2022-10-19 04:32:27,328] [INFO] (highdicom.seg.sop) - add plane #56 for segment #1\n",
      "[2022-10-19 04:32:27,330] [INFO] (highdicom.seg.sop) - add plane #57 for segment #1\n",
      "[2022-10-19 04:32:27,331] [INFO] (highdicom.seg.sop) - add plane #58 for segment #1\n",
      "[2022-10-19 04:32:27,333] [INFO] (highdicom.seg.sop) - add plane #59 for segment #1\n",
      "[2022-10-19 04:32:27,334] [INFO] (highdicom.seg.sop) - add plane #60 for segment #1\n",
      "[2022-10-19 04:32:27,336] [INFO] (highdicom.seg.sop) - add plane #61 for segment #1\n",
      "[2022-10-19 04:32:27,337] [INFO] (highdicom.seg.sop) - add plane #62 for segment #1\n",
      "[2022-10-19 04:32:27,339] [INFO] (highdicom.seg.sop) - add plane #63 for segment #1\n",
      "[2022-10-19 04:32:27,340] [INFO] (highdicom.seg.sop) - add plane #64 for segment #1\n",
      "[2022-10-19 04:32:27,342] [INFO] (highdicom.seg.sop) - add plane #65 for segment #1\n",
      "[2022-10-19 04:32:27,343] [INFO] (highdicom.seg.sop) - add plane #66 for segment #1\n",
      "[2022-10-19 04:32:27,345] [INFO] (highdicom.seg.sop) - add plane #67 for segment #1\n",
      "[2022-10-19 04:32:27,348] [INFO] (highdicom.seg.sop) - add plane #68 for segment #1\n",
      "[2022-10-19 04:32:27,349] [INFO] (highdicom.seg.sop) - add plane #69 for segment #1\n",
      "[2022-10-19 04:32:27,351] [INFO] (highdicom.seg.sop) - add plane #70 for segment #1\n",
      "[2022-10-19 04:32:27,352] [INFO] (highdicom.seg.sop) - add plane #71 for segment #1\n",
      "[2022-10-19 04:32:27,354] [INFO] (highdicom.seg.sop) - add plane #72 for segment #1\n",
      "[2022-10-19 04:32:27,355] [INFO] (highdicom.seg.sop) - add plane #73 for segment #1\n",
      "[2022-10-19 04:32:27,357] [INFO] (highdicom.seg.sop) - add plane #74 for segment #1\n",
      "[2022-10-19 04:32:27,358] [INFO] (highdicom.seg.sop) - add plane #75 for segment #1\n",
      "[2022-10-19 04:32:27,360] [INFO] (highdicom.seg.sop) - add plane #76 for segment #1\n",
      "[2022-10-19 04:32:27,361] [INFO] (highdicom.seg.sop) - add plane #77 for segment #1\n",
      "[2022-10-19 04:32:27,363] [INFO] (highdicom.seg.sop) - add plane #78 for segment #1\n",
      "[2022-10-19 04:32:27,365] [INFO] (highdicom.seg.sop) - add plane #79 for segment #1\n",
      "[2022-10-19 04:32:27,366] [INFO] (highdicom.seg.sop) - add plane #80 for segment #1\n",
      "[2022-10-19 04:32:27,368] [INFO] (highdicom.seg.sop) - add plane #81 for segment #1\n",
      "[2022-10-19 04:32:27,369] [INFO] (highdicom.seg.sop) - add plane #82 for segment #1\n",
      "[2022-10-19 04:32:27,371] [INFO] (highdicom.seg.sop) - add plane #83 for segment #1\n",
      "[2022-10-19 04:32:27,372] [INFO] (highdicom.seg.sop) - add plane #84 for segment #1\n",
      "[2022-10-19 04:32:27,374] [INFO] (highdicom.seg.sop) - add plane #85 for segment #1\n",
      "[2022-10-19 04:32:27,376] [INFO] (highdicom.seg.sop) - add plane #86 for segment #1\n",
      "[2022-10-19 04:32:27,377] [INFO] (highdicom.seg.sop) - add plane #87 for segment #1\n",
      "[2022-10-19 04:32:27,423] [INFO] (highdicom.base) - copy Image-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-19 04:32:27,423] [INFO] (highdicom.base) - copy attributes of module \"Specimen\"\n",
      "[2022-10-19 04:32:27,423] [INFO] (highdicom.base) - copy Patient-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-19 04:32:27,424] [INFO] (highdicom.base) - copy attributes of module \"Patient\"\n",
      "[2022-10-19 04:32:27,424] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Subject\"\n",
      "[2022-10-19 04:32:27,424] [INFO] (highdicom.base) - copy Study-related attributes from dataset \"1.3.6.1.4.1.14519.5.2.1.7085.2626.936983343951485811186213470191\"\n",
      "[2022-10-19 04:32:27,424] [INFO] (highdicom.base) - copy attributes of module \"General Study\"\n",
      "[2022-10-19 04:32:27,424] [INFO] (highdicom.base) - copy attributes of module \"Patient Study\"\n",
      "[2022-10-19 04:32:27,424] [INFO] (highdicom.base) - copy attributes of module \"Clinical Trial Study\"\n",
      "\u001b[34mDone performing execution of operator DICOMSegmentationWriterOperator\n",
      "\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "# Copy DICOM files are in 'dcm' folder\n",
    "\n",
    "# Launch the app\n",
    "!monai-deploy run my_app:latest dcm output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.2.826.0.1.3680043.10.511.3.11520264113032864635597744730480621.dcm\n",
      "1.2.826.0.1.3680043.10.511.3.12640275207784915071286249090159942.dcm\n",
      "1.2.826.0.1.3680043.10.511.3.60037419814661212759821378502636860.dcm\n",
      "1.2.826.0.1.3680043.10.511.3.62411810421842534426987578192611738.dcm\n",
      "prediction_output\n"
     ]
    }
   ],
   "source": [
    "!ls output"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.10 ('.venv': venv)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  },
  "vscode": {
   "interpreter": {
    "hash": "9b4ab1155d0cd1042497eb40fd55b2d15caf4b3c0f9fbfcc7ba4404045d40f12"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
