Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
44c65ed
Initial check-in for pipeline generator
mocsharp Aug 7, 2025
4445256
Remove duplicated MONAI models
mocsharp Aug 7, 2025
5eb6e4c
Add ImageOverlayWriter and update ImageDirectoryLoader
mocsharp Aug 8, 2025
2a0634a
Add support for Llama3-VILA-M3 models with new operators
mocsharp Aug 12, 2025
b531812
Update README and design documentation for pipeline generator
mocsharp Aug 13, 2025
8214e92
Bump version from 0.1.0 to 1.0.0 in pyproject.toml for pipeline-gener…
mocsharp Aug 13, 2025
7b1110e
Refactor operator imports and improve code formatting
mocsharp Aug 13, 2025
321d5b6
Add test pipeline generator workflow and bump version to 1.0.0
mocsharp Aug 13, 2025
d497bc6
Refactor ImageOverlayWriter documentation and enhance model_id valida…
mocsharp Aug 13, 2025
b720ce0
Enhance application documentation and refine requirements for pipelin…
mocsharp Aug 13, 2025
e2d8aa2
Refactor image loading operators and enhance directory scanning funct…
mocsharp Aug 15, 2025
0c207d5
Refactor operator imports and enhance pipeline generator functionality
mocsharp Aug 20, 2025
7e1f67f
Refactor operator imports and enhance code clarity
mocsharp Aug 20, 2025
22f0927
Fix formatting inconsistencies and improve error message clarity
mocsharp Aug 20, 2025
3a1da89
Remove deprecated test file for the pipeline generator
mocsharp Aug 20, 2025
3d6ea5b
Enhance type hinting and improve code clarity across operators
mocsharp Aug 20, 2025
502e352
Enhance pipeline generator functionality and improve bundle organization
mocsharp Aug 20, 2025
d0c1e33
Enhance bundle organization and improve model handling in pipeline ge…
mocsharp Aug 20, 2025
59de165
Refactor whitespace and improve code clarity in pipeline generator
mocsharp Aug 20, 2025
574191a
Enhance model handling and CLI functionality in pipeline generator
mocsharp Sep 10, 2025
0086d69
Fix formatting
mocsharp Sep 10, 2025
c7cdd48
Refactor model retrieval logic in HuggingFaceClient
mocsharp Sep 10, 2025
d9df3d4
Enhance model loading and configuration handling in MonaiBundleInfere…
mocsharp Sep 19, 2025
eea6879
Refactor whitespace and improve code clarity in MonaiBundleInferenceO…
mocsharp Sep 19, 2025
33901aa
Improve error handling in model loading for MonaiBundleInferenceOperator
mocsharp Sep 19, 2025
8e9914e
Enhance type hinting in MonaiBundleInferenceOperator
mocsharp Sep 19, 2025
5866be6
Refactor type hinting in MonaiBundleInferenceOperator
mocsharp Sep 19, 2025
94eafcd
Refactor MonaiBundleInferenceOperator for improved path handling and …
mocsharp Sep 24, 2025
b54c78d
Enhance image conversion logic in MonaiBundleInferenceOperator
mocsharp Sep 24, 2025
99b9f59
Pin monai<=1.5.0 since newly released 1.5.1 introduced breaking changes
MMelQin Sep 26, 2025
a735c45
Was wondering but did not expect the test is asseting on the value in…
MMelQin Sep 26, 2025
4e7768e
Use bundle_path property for consistency, and added warning note on n…
MMelQin Sep 26, 2025
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
19 changes: 19 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,22 @@ jobs:
with:
fail_ci_if_error: false
files: ./coverage.xml

test-pipeline-generator:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Install dependencies
working-directory: tools/pipeline-generator
run: |
uv sync
- name: Run tests
working-directory: tools/pipeline-generator
run: |
uv run pytest
332 changes: 320 additions & 12 deletions monai/deploy/operators/monai_bundle_inference_operator.py

Large diffs are not rendered by default.

42 changes: 41 additions & 1 deletion monai/deploy/operators/nii_data_loader_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@

# @md.env(pip_packages=["SimpleITK>=2.0.2"])
class NiftiDataLoader(Operator):
# NOTE:
Comment thread
MMelQin marked this conversation as resolved.
# This loader has very limited use because it does not capture or embed the metadata of a NIfTI file.
# Future improvements are needed to retrieve the metadata and return an Image object with the metadata,
# or use monai image transform to load the image and return a meta tensor.
# Also SITK GetArrayFromImage() returns an NDArray, and the newly added transpose here is to change the
# index ordering to match the index ordering of the DCOM pixel array. Existing client of this operator,
# if any, will likely have issues.
"""
This operator reads a nifti image, extracts the numpy array and forwards it to the next operator

Expand Down Expand Up @@ -80,7 +87,40 @@ def convert_and_save(self, nii_path):
image_reader = SimpleITK.ImageFileReader()
image_reader.SetFileName(str(nii_path))
image = image_reader.Execute()
image_np = np.transpose(SimpleITK.GetArrayFromImage(image), [2, 1, 0])
image_np = SimpleITK.GetArrayFromImage(image)

# Get image metadata to properly distinguish between different image types
spatial_dims = image.GetDimension() # Actual spatial dimensions (2D, 3D, etc.)
num_components = image.GetNumberOfComponentsPerPixel() # Components/channels per pixel

self._logger.debug(
f"Image spatial dimensions: {spatial_dims}, components per pixel: {num_components}, array shape: {image_np.shape}"
)

# Handle different dimensionalities properly using SimpleITK metadata
if spatial_dims == 2:
if num_components == 1:
# 2D grayscale: transpose from (y, x) to (x, y)
image_np = np.transpose(image_np, [1, 0])
else:
# 2D with multiple components/channels: transpose from (y, x, c) to (x, y, c)
# SimpleITK stores multi-component 2D images as (y, x, c)
image_np = np.transpose(image_np, [1, 0, 2])
elif spatial_dims == 3:
if num_components == 1:
# 3D grayscale volume: transpose from (z, y, x) to (x, y, z)
image_np = np.transpose(image_np, [2, 1, 0])
else:
# 3D volume with multiple components: transpose from (z, y, x, c) to (x, y, z, c)
# SimpleITK stores multi-component 3D images as (z, y, x, c)
image_np = np.transpose(image_np, [2, 1, 0, 3])
else:
# For other spatial dimensions, log a warning and return as-is
self._logger.warning(
f"Unexpected {spatial_dims}D spatial image with {num_components} components per pixel, "
f"array shape {image_np.shape} from {nii_path}, returning without transpose"
)

return image_np


Expand Down
2 changes: 2 additions & 0 deletions tools/pipeline-generator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
results*/
test_*/
248 changes: 248 additions & 0 deletions tools/pipeline-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# Pipeline Generator

A CLI tool for generating [MONAI Deploy](https://github.com/Project-MONAI/monai-deploy-app-sdk) application pipelines from [MONAI Bundles](https://docs.monai.io/en/stable/bundle_intro.html).

## Features

- List available MONAI models from HuggingFace
- Generate complete MONAI Deploy applications from HuggingFace models
- Support for multiple model sources through configuration
- Automatic bundle download and analysis
- Template-based code generation with Jinja2
- Beautiful output formatting with Rich (Python library for rich text and beautiful formatting)

## Platform Requirements

- **Linux/Unix operating systems only**
- Compatible with MONAI Deploy App SDK platform support
- Ubuntu 22.04+ recommended (aligned with main SDK requirements)

## Installation

```bash
# Clone the repository
cd tools/pipeline-generator/

# Install with uv (no virtualenv needed - uv manages it per command)
uv pip install -e ".[dev]"
```

### Running Commands

With uv, you can run commands directly without a prior "install" (pg is the Pipeline Generator command):

```bash
uv run pg --help
uv run pg list
uv run pg gen MONAI/spleen_ct_segmentation --output ./app
```

## Usage

### Complete Workflow Example

```bash
# 1. List available models
uv run pg list

# 2. Generate an application from a model
uv run pg gen MONAI/spleen_ct_segmentation --output my_app

# 3. Run the application
uv run pg run my_app --input /path/to/test/data --output ./results
Comment thread
mocsharp marked this conversation as resolved.
```

> [!NOTE]
> Input can be NIfTI files (.nii, .nii.gz) or DICOM series directories Refer to [config.yaml](tools/pipeline-generator/pipeline_generator/config/config.yaml) for details.

> [!NOTE]
> For DICOM input types, refer to the model documentation for DICOM series processing limitations.

### List Available Models

List all models from configured endpoints:

```bash
uv run pg list
```

Show only MONAI Bundles:

```bash
uv run pg list --bundles-only
```

Show only tested models:

```bash
uv run pg list --tested-only
```

Combine filters:

```bash
uv run pg list --bundles-only --tested-only # Show only tested MONAI Bundles
```

Use different output formats:

```bash
uv run pg list --format simple # Simple list format
uv run pg list --format json # JSON output
uv run pg list --format table # Default table format
```

Use a custom configuration file:

```bash
uv run pg --config /path/to/config.yaml list
```

### Generate MONAI Deploy Application

Generate an application from a HuggingFace model. Models are specified using the format `organization/model_name` (e.g., `MONAI/spleen_ct_segmentation`):

```bash
uv run pg gen MONAI/spleen_ct_segmentation --output my_app
```

Options:

- `--output, -o`: Output directory for generated app (default: ./output)
- `--app-name, -n`: Custom application class name (default: derived from model name)
- `--format`: Input/output data format (optional): auto, dicom, or nifti (default: auto)
Comment thread
mocsharp marked this conversation as resolved.
- For tested models, format is automatically detected from configuration
- For untested models, attempts detection from model metadata
- **DICOM Limitation**: Refer to the model documentation for multi-series support.
- `--force, -f`: Overwrite existing output directory

Generate with custom application class name:

```bash
uv run pg gen MONAI/lung_nodule_ct_detection --output lung_app --app-name LungDetectorApp
```

Force overwrite existing directory:

```bash
uv run pg gen MONAI/spleen_ct_segmentation --output test_app --force
```

Override data format (optional - auto-detected for tested models):

```bash
# Force DICOM format instead of auto-detection
uv run pg gen MONAI/some_model --output my_app --format dicom
```

### Run Generated Application

Run a generated application with automatic environment setup:

```bash
uv run pg run my_app --input /path/to/input --output /path/to/output
```

The `run` command will:

1. Create a virtual environment if it doesn't exist
1. Install dependencies from requirements.txt
1. Run the application with the specified input/output

Options:

- `--input, -i`: Input data directory (required)
- `--output, -o`: Output directory for results (default: ./output)
- `--model, -m`: Override model/bundle path
- `--venv-name`: Virtual environment directory name (default: .venv)
- `--skip-install`: Skip dependency installation
- `--gpu/--no-gpu`: Enable/disable GPU support (default: enabled)

Examples:

```bash
# Skip dependency installation (if already installed)
uv run pg run my_app --input test_data --output results --skip-install

# Run without GPU
uv run pg run my_app --input test_data --output results --no-gpu

# Use custom model path
uv run pg run my_app --input test_data --output results --model ./custom_model
```

## Configuration

The tool uses a YAML configuration file to define model sources. By default, it looks for `config.yaml` in the package directory.

Example configuration:

```yaml
# HuggingFace endpoints to scan for MONAI models
endpoints:
- organization: "MONAI"
base_url: "https://huggingface.co"
description: "Official MONAI organization models"

# Additional specific models not under the main organization
additional_models:
- model_id: "Project-MONAI/exaonepath"
base_url: "https://huggingface.co"
description: "ExaOnePath model for digital pathology"
```

## Generated Application Structure

When you run `pg gen`, it creates:

```
output/
├── app.py # Main application code
├── app.yaml # Configuration for MONAI Deploy packaging
├── requirements.txt # Python dependencies
├── README.md # Documentation
├── operators/ # Custom operators (if needed)
│ └── nifti_operators.py
└── model/ # Downloaded model files
├── configs/
├── models/
└── docs/
```

## Development

### Running Tests

```bash
# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=pipeline_generator

# Run specific test file
uv run pytest tests/test_cli.py
```

### Code Quality

```bash
# Format code
uv run black pipeline_generator tests

# Lint code
uv run flake8 pipeline_generator tests

# Type checking
uv run mypy pipeline_generator
```

## Future Commands

The CLI is designed to be extensible. Planned commands include:

- `pg package <app>` - Package an application using the Holoscan CLI packaging tool

## License

This project is part of the MONAI Deploy App SDK and is licensed under the Apache License 2.0. See the main repository's LICENSE file for details.
Loading