Skip to content

Conversation

@gigony
Copy link
Collaborator

@gigony gigony commented Sep 13, 2021

  • Refactor AppContext to be updatable
  • Set up logging level in Application.run()
  • Accept arguments in Application.run()
  • Do not run Application if no arguments are specified in
    Application.run() method
  • Create an output folder if not exists, before executing the app.

With this PR, the below code is possible with Jupyter Notebook.

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

from monai.deploy.core import (
    Application,
    DataPath,
    ExecutionContext,
    Image,
    InputContext,
    IOType,
    Operator,
    OutputContext,
    env,
    input,
    output,
    resource,
)
from monai.transforms import AddChannel, Compose, EnsureType, ScaleIntensity

MEDNIST_CLASSES = ["AbdomenCT", "BreastMRI", "CXR", "ChestCT", "Hand", "HeadCT"]


@input("image", DataPath, IOType.DISK)
@output("image", Image, IOType.IN_MEMORY)
@env(pip_packages=["pillow"])
class LoadPILOperator(Operator):
    """Load image from the given input (DataPath) and set numpy array to the output (Image)."""

    def compute(self, input: InputContext, output: OutputContext, context: ExecutionContext):
        import numpy as np
        from PIL import Image as PILImage

        input_path = input.get().path

        image = PILImage.open(input_path)
        image = image.convert("L")  # convert to greyscale image
        image_arr = np.asarray(image)

        output_image = Image(image_arr)  # create Image domain object with a numpy array
        output.set(output_image)


@input("image", Image, IOType.IN_MEMORY)
@output("output", DataPath, IOType.DISK)
@env(pip_packages=["monai"])
class MedNISTClassifierOperator(Operator):
    """Classifies the given image and returns the class name."""

    @property
    def transform(self):
        return Compose([AddChannel(), ScaleIntensity(), EnsureType()])

    def compute(self, input: InputContext, output: OutputContext, context: ExecutionContext):
        import json

        import torch

        img = input.get().asnumpy()  # (64, 64), uint8
        image_tensor = self.transform(img)  # (1, 64, 64), torch.float64
        image_tensor = image_tensor[None].float()  # (1, 1, 64, 64), torch.float32

        # Comment below line if you want to do CPU inference
        image_tensor = image_tensor.cuda()

        model = context.models.get()  # get a TorchScriptModel object
        # Uncomment the following line if you want to do CPU inference
        # model.predictor = torch.jit.load(model.path, map_location="cpu").eval()

        with torch.no_grad():
            outputs = model(image_tensor)

        _, output_classes = outputs.max(dim=1)

        result = MEDNIST_CLASSES[output_classes[0]]  # get the class name
        print(result)

        # Get output (folder) path and create the folder if not exists
        output_folder = output.get().path
        output_folder.mkdir(parents=True, exist_ok=True)

        # Write result to "output.json"
        output_path = output_folder / "output.json"
        with open(output_path, "w") as fp:
            json.dump(result, fp)


@resource(cpu=1, memory="1Gi")
class App(Application):
    """Application class for the MedNIST classifier."""

    def compose(self):
        load_pil_op = LoadPILOperator()
        classifier_op = MedNISTClassifierOperator()

        self.add_flow(load_pil_op, classifier_op)


if __name__ == "__main__":
    App(do_run=True)
app = App()
app.context
AppContext(graph=nx_digraph, input_path=input, output_path=output, model_path=models, workdir=, datastore=memory, executor=single_process_executor, resource=Resource(cpu=1, memory=1073741824, gpu=0))
app.run(input="input/AbdomenCT_007000.jpeg", model="classifier.zip")
�[34mGoing to initiate execution of operator LoadPILOperator�[39m
�[32mExecuting operator LoadPILOperator �[33m(Process ID: 24042, Operator ID: f68514e8-36bd-4d69-a668-36f96be9713b)�[39m
�[34mDone performing execution of operator LoadPILOperator
�[39m
�[34mGoing to initiate execution of operator MedNISTClassifierOperator�[39m
�[32mExecuting operator MedNISTClassifierOperator �[33m(Process ID: 24042, Operator ID: f5341505-f058-4531-b2ef-06f18c09a411)�[39m
AbdomenCT
�[34mDone performing execution of operator MedNISTClassifierOperator
�[39m
app.context
AppContext(graph=nx_digraph, input_path=/home/gbae/repo/monai-deploy-app-sdk/input/AbdomenCT_007000.jpeg, output_path=output, model_path=classifier.zip, workdir=, datastore=memory, executor=single_process_executor, resource=Resource(cpu=0, memory=0, gpu=0))

Closes #82

@gigony gigony added the architectural story Work related to architecture label Sep 13, 2021
@gigony gigony added this to the v0.1.0 milestone Sep 13, 2021
@gigony gigony requested a review from MMelQin September 13, 2021 01:58
@gigony gigony self-assigned this Sep 13, 2021
- Refactor AppContext to be updatable
- Set up logging level in Application.run()
- Accept arguments in Application.run()
- Do not run Application if no arguments are specified in
  Application.run() method
- Create output folder if not exists, before executing the app.

Signed-off-by: Gigon Bae <gbae@nvidia.com>
Copy link
Collaborator

@MMelQin MMelQin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look good

@gigony gigony merged commit 04918a6 into main Sep 14, 2021
@MMelQin MMelQin deleted the allow_prog_io branch November 5, 2025 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

architectural story Work related to architecture

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow setting input parameters(input/output/model path) programmatically

3 participants