Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SAM2 (Inference only) as base model for all the apps #1777

Merged
merged 10 commits into from
Nov 7, 2024
Prev Previous commit
Next Next commit
Support SAM2 for python >=3.10
Signed-off-by: Sachidanand Alle <salle@nvidia.com>
SachidanandAlle committed Nov 6, 2024
commit 7523daf66eab8f84919d74dff560d4ae28e43815
9 changes: 9 additions & 0 deletions monailabel/sam2/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from monai.utils import optional_import


def is_sam2_module_available():
try:
_, flag = optional_import("sam2")
return flag
except ImportError:
return False
8 changes: 7 additions & 1 deletion plugins/cvat/README.md
Original file line number Diff line number Diff line change
@@ -57,8 +57,9 @@ chmod +x nuctl-$NUCLIO_VERSION-linux-amd64
ln -sf $(pwd)/nuctl-$NUCLIO_VERSION-linux-amd64 /usr/local/bin/nuctl
```

#### Deployment of Endoscopy Models
#### Deployment of Endoscopy/SAM2 Models
This step is to deploy MONAI Label plugin with endoscopic models using Nuclio tool.
> **Prerequisite:** MONAI Label Server is up and running for _**endoscopy**_ app.

```bash
# Run MONAI Label Server (Make sure this Host/IP is accessible inside a docker)
@@ -68,8 +69,13 @@ git clone https://github.com/Project-MONAI/MONAILabel.git

# Deploy all endoscopy models
./plugins/cvat/deploy.sh endoscopy

# Or to deploy specific function and model, e.g., tooltracking
./plugins/cvat/deploy.sh endoscopy tooltracking

# Deploy SAM2 Interactor
./plugins/cvat/deploy.sh sam2 interactor

```

After model deployment, you can see the model names in the `Models` page of CVAT.
2 changes: 1 addition & 1 deletion plugins/cvat/pathology/deepedit_nuclei.yaml
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ spec:
directives:
preCopy:
- kind: ENV
value: MONAI_LABEL_SERVER=http://10.117.9.63:8000
value: MONAI_LABEL_SERVER=http://monailabel.com
- kind: ENV
value: MONAI_LABEL_MODEL=deepedit_nuclei

2 changes: 1 addition & 1 deletion plugins/cvat/pathology/nuclick.yaml
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ spec:
directives:
preCopy:
- kind: ENV
value: MONAI_LABEL_SERVER=http://10.117.9.63:8000
value: MONAI_LABEL_SERVER=http://monailabel.com
- kind: ENV
value: MONAI_LABEL_MODEL=nuclick

2 changes: 1 addition & 1 deletion plugins/cvat/pathology/segmentation_nuclei.yaml
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ spec:
directives:
preCopy:
- kind: ENV
value: MONAI_LABEL_SERVER=http://10.117.9.63:8000
value: MONAI_LABEL_SERVER=http://monailabel.com
- kind: ENV
value: MONAI_LABEL_MODEL=segmentation_nuclei

4 changes: 2 additions & 2 deletions plugins/cvat/sam2/interactor.yaml
Original file line number Diff line number Diff line change
@@ -34,9 +34,9 @@ spec:
directives:
preCopy:
- kind: ENV
value: MONAI_LABEL_SERVER=http://10.117.9.63:8000
value: MONAI_LABEL_SERVER=http://monailabel.com
- kind: ENV
value: MONAI_LABEL_MODEL=sam2
value: MONAI_LABEL_MODEL=sam_2d

triggers:
myHttpTrigger:
4 changes: 2 additions & 2 deletions plugins/cvat/sam2/tracker.yaml
Original file line number Diff line number Diff line change
@@ -30,9 +30,9 @@ spec:
directives:
preCopy:
- kind: ENV
value: MONAI_LABEL_SERVER=http://10.117.9.63:8000
value: MONAI_LABEL_SERVER=http://monailabel.com
- kind: ENV
value: MONAI_LABEL_MODEL=sam2
value: MONAI_LABEL_MODEL=sam_2d

triggers:
myHttpTrigger:
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ urllib3==2.2.2
scikit-learn
scipy
google-auth==2.29.0
git+https://github.com/facebookresearch/sam2.git@c2ec8e14a185632b0a5d8b161928ceb50197eddc
git+https://github.com/facebookresearch/sam2.git@c2ec8e14a185632b0a5d8b161928ceb50197eddc ; python_version >= '3.10'


# scipy and scikit-learn latest packages are missing on python 3.8
8 changes: 8 additions & 0 deletions sample-apps/README.md
Original file line number Diff line number Diff line change
@@ -54,6 +54,14 @@ The endoscopy template includes example models for interactive and automated too
#### [MONAI Bundle](./monaibundle)
The MONAI Bundle format provides a portable description of deep learning models. This template includes example models for interactive and automated segmentation using MONAI bundles defined in the MONAI Model Zoo. It can pull any bundle defined in the MONAI Model Zoo that is compatible and meets the requirements specified on the [MONAI Bundle Apps page](./monaibundle/).

----
> [**SAM2**](https://github.com/facebookresearch/sam2/)
>
> By default, SAM2 is available **_(python >= 3.10)_** for all the above Apps
> - **sam_2d**: for any organ or tissue and others.
> - **sam_3d**: in case of Radiology/MonaiBundle apps to support SAM2 propagation over multiple slices.


### Creating a Custom App
Researchers may want to define and add their own models. Follow the steps below to add a new segmentation model:

8 changes: 5 additions & 3 deletions sample-apps/endoscopy/main.py
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@
from monailabel.interfaces.tasks.scoring import ScoringMethod
from monailabel.interfaces.tasks.strategy import Strategy
from monailabel.interfaces.tasks.train import TrainTask
from monailabel.sam2.infer import Sam2InferTask
from monailabel.sam2.utils import is_sam2_module_available
from monailabel.tasks.activelearning.random import Random
from monailabel.utils.others.class_utils import get_class_names
from monailabel.utils.others.generic import create_dataset_from_path, strtobool
@@ -131,8 +131,10 @@ def init_infers(self) -> Dict[str, InferTask]:
#################################################
# SAM
#################################################
if self.sam:
infers["sam2"] = Sam2InferTask(
if is_sam2_module_available() and self.sam:
from monailabel.sam2.infer import Sam2InferTask

infers["sam_2d"] = Sam2InferTask(
model_dir=self.model_dir,
type=InferType.ANNOTATION,
dimension=2,
6 changes: 4 additions & 2 deletions sample-apps/monaibundle/main.py
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
from monailabel.interfaces.tasks.scoring import ScoringMethod
from monailabel.interfaces.tasks.strategy import Strategy
from monailabel.interfaces.tasks.train import TrainTask
from monailabel.sam2.infer import Sam2InferTask
from monailabel.sam2.utils import is_sam2_module_available
from monailabel.tasks.activelearning.first import First
from monailabel.tasks.activelearning.random import Random
from monailabel.tasks.infer.bundle import BundleInferTask
@@ -80,7 +80,9 @@ def init_infers(self) -> Dict[str, InferTask]:
#################################################
# SAM
#################################################
if self.sam:
if is_sam2_module_available() and self.sam:
from monailabel.sam2.infer import Sam2InferTask

infers["sam_2d"] = Sam2InferTask(model_dir=self.model_dir, dimension=2)
infers["sam_3d"] = Sam2InferTask(model_dir=self.model_dir, dimension=3)
return infers
8 changes: 5 additions & 3 deletions sample-apps/pathology/main.py
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@
from monailabel.interfaces.tasks.infer_v2 import InferTask, InferType
from monailabel.interfaces.tasks.strategy import Strategy
from monailabel.interfaces.tasks.train import TrainTask
from monailabel.sam2.infer import Sam2InferTask
from monailabel.sam2.utils import is_sam2_module_available
from monailabel.tasks.infer.basic_infer import BasicInferTask
from monailabel.transform.post import FindContoursd
from monailabel.transform.writer import PolygonWriter
@@ -146,8 +146,10 @@ def init_infers(self) -> Dict[str, InferTask]:
#################################################
# SAM
#################################################
if self.sam:
infers["sam2"] = Sam2InferTask(
if is_sam2_module_available() and self.sam:
from monailabel.sam2.infer import Sam2InferTask

infers["sam_2d"] = Sam2InferTask(
model_dir=self.model_dir,
type=InferType.ANNOTATION,
dimension=2,
6 changes: 4 additions & 2 deletions sample-apps/radiology/main.py
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@
from monailabel.interfaces.tasks.scoring import ScoringMethod
from monailabel.interfaces.tasks.strategy import Strategy
from monailabel.interfaces.tasks.train import TrainTask
from monailabel.sam2.infer import Sam2InferTask
from monailabel.sam2.utils import is_sam2_module_available
from monailabel.tasks.activelearning.first import First
from monailabel.tasks.activelearning.random import Random

@@ -168,7 +168,9 @@ def init_infers(self) -> Dict[str, InferTask]:
#################################################
# SAM
#################################################
if self.sam:
if is_sam2_module_available() and self.sam:
from monailabel.sam2.infer import Sam2InferTask

infers["sam_2d"] = Sam2InferTask(model_dir=self.model_dir, dimension=2)
infers["sam_3d"] = Sam2InferTask(model_dir=self.model_dir, dimension=3)

2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ install_requires =
scikit-learn
scipy
google-auth>=2.29.0
git+https://github.com/facebookresearch/sam2.git@c2ec8e14a185632b0a5d8b161928ceb50197eddc
git+https://github.com/facebookresearch/sam2.git@c2ec8e14a185632b0a5d8b161928ceb50197eddc ; python_version >= '3.10'

[flake8]
select = B,C,E,F,N,P,T4,W,B9
6 changes: 4 additions & 2 deletions tests/unit/endpoints/test_infer_v2.py
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@

import torch

from monailabel.sam2.utils import is_sam2_module_available

from .context import BasicBundleTestSuite, BasicDetectionBundleTestSuite, BasicEndpointV2TestSuite


@@ -43,7 +45,7 @@ def test_deepgrow_pipeline(self):
time.sleep(1)

def test_sam_2d(self):
if not torch.cuda.is_available():
if not is_sam2_module_available() or not torch.cuda.is_available():
return

model = "sam_2d"
@@ -55,7 +57,7 @@ def test_sam_2d(self):
time.sleep(1)

def test_sam_3d(self):
if not torch.cuda.is_available():
if not is_sam2_module_available or not torch.cuda.is_available():
return

model = "sam_3d"