Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pip install monai-deploy-app-sdk

### Prerequisites

- This SDK depends on [NVIDIA Holoscan SDK](https://pypi.org/project/holoscan/) for its core implementation as well as its CLI, hence inherits its prerequisites, e.g. Ubuntu 22.04 with glibc 2.35 on X86-64 and NVIDIA dGPU drivers version 535 or above.
- This SDK depends on [NVIDIA Holoscan SDK](https://pypi.org/project/holoscan/) for its core implementation as well as its CLI, hence inherits its prerequisites, e.g. Ubuntu 22.04 with glibc 2.35 on X86-64 and NVIDIA dGPU drivers version 535 or above. Important to note that `holoscan` and `holoscan-cli` up to v4.2 are compatible.
- Key runtime dependencies also include [nvidia-nvimgcodec](https://pypi.org/project/nvidia-nvimgcodec-cu12/) and its own dependencies for GPU accecelerated DICOM image decoding.
- [CUDA 12.2](https://developer.nvidia.com/cuda-12-2-0-download-archive) or above is required along with a supported NVIDIA GPU with at least 8GB of video RAM.
- If inference is not used in an example application and a GPU is not installed, at least [CUDA 12 runtime](https://pypi.org/project/nvidia-cuda-runtime-cu12/) is required, as this is one of the requirements of Holoscan SDK. In addition, the `LIB_LIBRARY_PATH` must be set to include the installed shared library, e.g. in a Python 3.10 env, ```export LD_LIBRARY_PATH=`pwd`/.venv/lib/python3.10/site-packages/nvidia/cuda_runtime/lib:$LD_LIBRARY_PATH```
- Python: 3.10 to 3.13
Expand Down Expand Up @@ -80,20 +81,13 @@ Tutorials are provided to help getting started with the App SDK, to name but a f

#### [2) Creating MedNIST Classifier app](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/mednist_app.html)

YouTube Video (to be updated with the new version):

- [MedNIST Classification Example](https://www.youtube.com/watch?v=WwjilJFHuU4)

### [3) Creating a Segmentation app](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/segmentation_app.html)

YouTube Video (demonstrating the previous version of the App SDK):

- [Spleen Organ Segmentation - Jupyter Notebook Tutorial](https://www.youtube.com/watch?v=cqDVxzYt9lY)
- [Spleen Organ Segmentation - Deep Dive](https://www.youtube.com/watch?v=nivgfD4pwWE)
#### [3) Creating a Segmentation app](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/segmentation_app.html)

### [4) Creating a Segmentation app including visualization with Clara Viz](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/segmentation_clara-viz_app.html)
#### [4) Creating a Segmentation app including visualization with Clara Viz](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/segmentation_clara-viz_app.html)

### [5) Creating a Segmentation app consuming a MONAI Bundle](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/monai_bundle_app.html)
#### [5) Creating a Segmentation app consuming a MONAI Bundle](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/tutorials/monai_bundle_app.html)

### [Examples](https://monai.readthedocs.io/projects/monai-deploy-app-sdk/en/stable/getting_started/examples.html)

Expand Down
2 changes: 1 addition & 1 deletion monai/deploy/core/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from monai.deploy.exceptions import ItemNotExistsError, UnknownTypeError

# Store all supported model types in the order they should be checked
REGISTERED_MODELS = []
REGISTERED_MODELS: List[type["Model"]] = []


class Model:
Expand Down
30 changes: 29 additions & 1 deletion monai/deploy/graphs/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
from holoscan.graphs import *
import importlib

try:
_flow_graphs = importlib.import_module("holoscan.flow_graphs")
except ModuleNotFoundError as e:
if e.name != "holoscan.flow_graphs":
raise
_graphs = importlib.import_module("holoscan.graphs")
FlowGraph = _graphs.FlowGraph
FragmentFlowGraph = _graphs.FragmentFlowGraph
OperatorFlowGraph = _graphs.OperatorFlowGraph

__all__ = ["FlowGraph", "FragmentFlowGraph", "OperatorFlowGraph"]
else:
FlowGraphImpl = _flow_graphs.FlowGraphImpl
FragmentFlowGraph = _flow_graphs.FragmentFlowGraph
FragmentFlowGraphImpl = _flow_graphs.FragmentFlowGraphImpl
OperatorFlowGraph = _flow_graphs.OperatorFlowGraph
OperatorFlowGraphImpl = _flow_graphs.OperatorFlowGraphImpl
FlowGraph = FlowGraphImpl

__all__ = [
"FlowGraph",
"FlowGraphImpl",
"FragmentFlowGraph",
"FragmentFlowGraphImpl",
"OperatorFlowGraph",
"OperatorFlowGraphImpl",
]
14 changes: 9 additions & 5 deletions monai/deploy/operators/monet_bundle_inference_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Dict, Tuple, Union
from collections.abc import Hashable, Mapping
from typing import Any, Dict, Tuple, Union, cast

from monai.deploy.core import Image
from monai.deploy.operators.monai_bundle_inference_operator import MonaiBundleInferenceOperator, get_bundle_config
from monai.deploy.utils.importutil import optional_import
from monai.transforms import ConcatItemsd, ResampleToMatch
from monai.deploy.core.models.torch_model import TorchScriptModel
from monai.deploy.core.models.triton_model import TritonModel
from monai.deploy.operators.monai_bundle_inference_operator import MonaiBundleInferenceOperator
from monai.deploy.utils.importutil import optional_import
from monai.transforms import ConcatItemsd, ResampleToMatch

torch, _ = optional_import("torch", "1.10.2")
MetaTensor, _ = optional_import("monai.data.meta_tensor", name="MetaTensor")
__all__ = ["MONetBundleInferenceOperator"]
Expand Down Expand Up @@ -88,7 +90,9 @@ def predict(self, data: Any, *args, **kwargs) -> Union[Image, Any, Tuple[Any, ..
for key in kwargs.keys():
if isinstance(kwargs[key], MetaTensor):
multimodal_data[key] = ResampleToMatch(mode="bilinear")(kwargs[key], img_dst=data)
data = ConcatItemsd(keys=list(multimodal_data.keys()), name="image")(multimodal_data)["image"]
data = ConcatItemsd(keys=list(multimodal_data.keys()), name="image")(
cast(Mapping[Hashable, Any], multimodal_data)
)["image"]
if len(data.shape) == 4:
data = data[None]
prediction = self._nnunet_predictor(data)
Expand Down
58 changes: 46 additions & 12 deletions monai/deploy/utils/importutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import importlib.util
import inspect
import re
import runpy
Expand Down Expand Up @@ -411,7 +412,44 @@ def dist_requires(project_name: str) -> List[str]:
return []


holoscan_init_content_txt = """
def _holoscan_package_path() -> Path:
"""Return the installed holoscan package directory without importing holoscan."""
spec = importlib.util.find_spec("holoscan")
if spec is None:
raise ModuleNotFoundError("Holoscan is not installed; cannot locate the holoscan package directory.")
if spec.submodule_search_locations:
return Path(spec.submodule_search_locations[0]).resolve()
if spec.origin is None:
raise ModuleNotFoundError("Holoscan package spec has no origin or submodule_search_locations.")
return Path(spec.origin).resolve().parent


def _holoscan_graph_module_name(holoscan_pkg_path: Path) -> Optional[str]:
"""Return the graph submodule name present in the installed Holoscan package."""
if (holoscan_pkg_path / "flow_graphs").is_dir():
return "flow_graphs"
if (holoscan_pkg_path / "graphs").is_dir():
return "graphs"
return None


def _build_holoscan_extra_modules(holoscan_pkg_path: Path) -> list[str]:
extra_modules = [
"conditions",
"executors",
"logger",
"operators",
"resources",
]
graph_module = _holoscan_graph_module_name(holoscan_pkg_path)
if graph_module is not None:
extra_modules.insert(2, graph_module)
return extra_modules


def _build_holoscan_init_content(extra_modules: list[str]) -> str:
extra_modules_repr = ",\n ".join(f"{name!r}" for name in extra_modules)
return f"""\
# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
Expand All @@ -435,12 +473,7 @@ def dist_requires(project_name: str) -> List[str]:

# Other modules are exposed to the public API but will only be lazily loaded
_EXTRA_MODULES = [
"conditions",
"executors",
"graphs",
"logger",
"operators",
"resources",
{extra_modules_repr},
]
__all__.extend(_EXTRA_MODULES)

Expand All @@ -456,12 +489,12 @@ def __getattr__(name):
import sys

if name in _EXTRA_MODULES:
module_name = f"{__name__}.{name}"
module_name = f"{{__name__}}.{{name}}"
module = importlib.import_module(module_name) # import
sys.modules[module_name] = module # cache
return module
else:
raise AttributeError(f"module {__name__} has no attribute {name}")
raise AttributeError(f"module {{__name__}} has no attribute {{name}}")

"""

Expand All @@ -470,11 +503,12 @@ def fix_holoscan_import():
"""Fix holoscan __init__ to enable lazy load for avoiding failure on loading low level libs."""

try:
project_name = "holoscan"
holoscan_init_path = Path(dist_module_path(project_name)) / project_name / "__init__.py"
holoscan_pkg_path = _holoscan_package_path()
holoscan_init_path = holoscan_pkg_path / "__init__.py"
extra_modules = _build_holoscan_extra_modules(holoscan_pkg_path)

with open(str(holoscan_init_path), "w") as f_w:
f_w.write(holoscan_init_content_txt)
f_w.write(_build_holoscan_init_content(extra_modules))
return str(holoscan_init_path)
except Exception as ex:
return ex
Expand Down
1,466 changes: 49 additions & 1,417 deletions notebooks/tutorials/01_simple_app.ipynb

Large diffs are not rendered by default.

Loading
Loading