Skip to content

Commit bc43128

Browse files
500 Fix deepedit orientation issue [skip mgpu] (Project-MONAI#501)
Fixes Project-MONAI#500 . ### Description This PR is used to fix the orientation issue of the spleen deepedit annotation bundle. This PR refers to Project-MONAI/MONAILabel#1537 ### Status **Ready** ### Please ensure all the checkboxes: <!--- Put an `x` in all the boxes that apply, and remove the not applicable items --> - [x] Codeformat tests passed locally by running `./runtests.sh --codeformat`. - [ ] In-line docstrings updated. - [ ] Update `version` and `changelog` in `metadata.json` if changing an existing bundle. - [ ] Please ensure the naming rules in config files meet our requirements (please refer to: `CONTRIBUTING.md`). - [ ] Ensure versions of packages such as `monai`, `pytorch` and `numpy` are correct in `metadata.json`. - [ ] Descriptions should be consistent with the content, such as `eval_metrics` of the provided weights and TorchScript modules. - [ ] Files larger than 25MB are excluded and replaced by providing download links in `large_file.yml`. - [ ] Avoid using path that contains personal information within config files (such as use `/home/your_name/` for `"bundle_root"`). --------- Signed-off-by: Yiheng Wang <vennw@nvidia.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 580b134 commit bc43128

File tree

7 files changed

+59
-55
lines changed

7 files changed

+59
-55
lines changed

ci/download_latest_bundle.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
def download_latest_bundle(bundle_name: str, models_path: str, download_path: str):
2121
model_info_path = os.path.join(models_path, "model_info.json")
2222
version = get_latest_version(bundle_name=bundle_name, model_info_path=model_info_path)
23-
download(name=bundle_name, source="github", version=version, bundle_dir=download_path)
23+
24+
download(name=bundle_name, source="monaihosting", version=version, bundle_dir=download_path)
2425

2526

2627
if __name__ == "__main__":

ci/run_premerge_cpu.sh

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,54 +29,38 @@ elif [[ $# -gt 1 ]]; then
2929
exit 1
3030
fi
3131

32-
init_pipenv() {
33-
echo "initializing pip environment: $1"
34-
pipenv install --python=3.8 -r $1
35-
export PYTHONPATH=$PWD
36-
}
37-
38-
remove_pipenv() {
39-
echo "removing pip environment"
40-
pipenv --rm
41-
rm Pipfile Pipfile.lock
42-
pipenv --clear
43-
df -h
44-
}
45-
4632
verify_bundle() {
4733
echo 'Run verify bundle...'
48-
init_pipenv requirements.txt
34+
pip install -r requirements.txt
4935
head_ref=$(git rev-parse HEAD)
5036
git fetch origin dev $head_ref
5137
# achieve all changed files in 'models'
5238
changes=$(git diff --name-only $head_ref origin/dev -- models)
5339
if [ ! -z "$changes" ]
5440
then
5541
# get all changed bundles
56-
bundle_list=$(pipenv run python $(pwd)/ci/get_changed_bundle.py --f "$changes")
42+
bundle_list=$(python $(pwd)/ci/get_changed_bundle.py --f "$changes")
5743
if [ ! -z "$bundle_list" ]
5844
then
59-
pipenv run python $(pwd)/ci/prepare_schema.py --l "$bundle_list"
45+
python $(pwd)/ci/prepare_schema.py --l "$bundle_list"
6046
echo $bundle_list
6147
for bundle in $bundle_list;
6248
do
63-
init_pipenv requirements-dev.txt
49+
pip install -r requirements-dev.txt
6450
# get required libraries according to the bundle's metadata file
65-
requirements=$(pipenv run python $(pwd)/ci/get_bundle_requirements.py --b "$bundle")
51+
requirements=$(python $(pwd)/ci/get_bundle_requirements.py --b "$bundle")
6652
if [ ! -z "$requirements" ]; then
6753
echo "install required libraries for bundle: $bundle"
68-
pipenv install -r "$requirements"
54+
pip install -r "$requirements"
6955
fi
7056
# verify bundle
71-
pipenv run python $(pwd)/ci/verify_bundle.py -b "$bundle" -m "min" # min tests on cpu
72-
remove_pipenv
57+
python $(pwd)/ci/verify_bundle.py -b "$bundle" -m "min" # min tests on cpu
7358
done
7459
else
7560
echo "this pull request does not change any bundles, skip verify."
7661
fi
7762
else
7863
echo "this pull request does not change any files in 'models', skip verify."
79-
remove_pipenv
8064
fi
8165
}
8266

ci/unit_tests/test_spleen_deepedit_annotation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import os
1313
import shutil
14+
import sys
1415
import tempfile
1516
import unittest
1617

@@ -123,12 +124,11 @@ def test_infer_config(self, override):
123124
@parameterized.expand([TEST_CASE_2])
124125
def test_infer_click_config(self, override):
125126
override["dataset_dir"] = self.dataset_dir
126-
override["use_click"] = True
127127
override[
128128
"dataset#data"
129129
] = "$[{'image': i, 'background': [], 'spleen': [[6, 6, 6], [8, 8, 8]]} for i in @datalist]"
130130
bundle_root = override["bundle_root"]
131-
print(override)
131+
sys.path = [bundle_root] + sys.path
132132

133133
inferrer = ConfigWorkflow(
134134
workflow="infer",

models/spleen_deepedit_annotation/configs/inference.json

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
],
2020
"number_intensity_ch": 1,
2121
"device": "$torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')",
22-
"use_click": false,
2322
"network_def": {
2423
"_target_": "DynUNet",
2524
"spatial_dims": 3,
@@ -86,21 +85,12 @@
8685
"clip": true
8786
}
8887
],
89-
"auto_seg_transforms": [
88+
"deepedit_transforms": [
9089
{
91-
"_target_": "Resized",
92-
"keys": "image",
93-
"spatial_size": "@spatial_size",
94-
"mode": "area"
90+
"_target_": "scripts.transforms.OrientationGuidanceMultipleLabelDeepEditd",
91+
"ref_image": "image",
92+
"label_names": "@label_names"
9593
},
96-
{
97-
"_target_": "DiscardAddGuidanced",
98-
"keys": "image",
99-
"label_names": "@label_names",
100-
"number_intensity_ch": "@number_intensity_ch"
101-
}
102-
],
103-
"deepedit_transforms": [
10494
{
10595
"_target_": "AddGuidanceFromPointsDeepEditd",
10696
"ref_image": "image",
@@ -133,7 +123,7 @@
133123
],
134124
"preprocessing": {
135125
"_target_": "Compose",
136-
"transforms": "$@preprocessing_transforms + (@deepedit_transforms if @use_click else @auto_seg_transforms) + @extra_transforms"
126+
"transforms": "$@preprocessing_transforms + @deepedit_transforms + @extra_transforms"
137127
},
138128
"dataset": {
139129
"_target_": "Dataset",

models/spleen_deepedit_annotation/configs/metadata.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"schema": "https://github.com/Project-MONAI/MONAI-extra-test-data/releases/download/0.8.1/meta_schema_20220324.json",
3-
"version": "0.4.8",
3+
"version": "0.4.9",
44
"changelog": {
5+
"0.4.9": "fix orientation issue on clicks",
56
"0.4.8": "Add infer transforms to manage clicks from viewer",
67
"0.4.7": "fix the wrong GPU index issue of multi-node",
78
"0.4.6": "update to use rc7 which solves dynunet issue",
@@ -31,7 +32,8 @@
3132
"optional_packages_version": {
3233
"itk": "5.3.0",
3334
"pytorch-ignite": "0.4.9",
34-
"scikit-image": "0.19.3"
35+
"scikit-image": "0.19.3",
36+
"einops": "0.6.1"
3537
},
3638
"name": "Spleen DeepEdit annotation",
3739
"task": "Decathlon spleen segmentation",

models/spleen_deepedit_annotation/docs/README.md

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,11 @@ python -m monai.bundle run --config_file "['configs/train.json','configs/evaluat
118118

119119
#### Execute inference:
120120

121-
122-
For automatic inference mode:
123-
124-
125121
```
126122
python -m monai.bundle run --config_file configs/inference.json
127123
```
128124

129-
For interactive segmentation mode, in which the user provides clicks, set the **use_click** flag to true:
130-
131-
132-
```
133-
python -m monai.bundle run --config_file configs/inference.json --use_click true
134-
```
135-
136-
Clicks should be added to the data dictionary that is passed to the preprocessing transforms. The add keys are defined in `label_names` in `configs/inference.json`, and the corresponding values are the point coordinates. The following is an example of a data dictionary:
125+
Optionally, clicks can be added to the data dictionary that is passed to the preprocessing transforms. The add keys are defined in `label_names` in `configs/inference.json`, and the corresponding values are the point coordinates. The following is an example of a data dictionary:
137126

138127
```
139128
{"image": "example.nii.gz", "background": [], "spleen": [[I1, J1, K1], [I2, J2, K2]]}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import Dict
2+
3+
import numpy as np
4+
from einops import rearrange
5+
from monai.transforms.transform import Transform
6+
7+
8+
class OrientationGuidanceMultipleLabelDeepEditd(Transform):
9+
def __init__(self, ref_image="image", label_names=None):
10+
"""
11+
Convert the guidance to the RAS orientation
12+
"""
13+
self.ref_image = ref_image
14+
self.label_names = label_names
15+
16+
def transform_points(self, point, affine):
17+
"""transform point to the coordinates of the transformed image
18+
point: numpy array [bs, N, 3]
19+
"""
20+
bs, n = point.shape[:2]
21+
point = np.concatenate((point, np.ones((bs, n, 1))), axis=-1)
22+
point = rearrange(point, "b n d -> d (b n)")
23+
point = affine @ point
24+
point = rearrange(point, "d (b n)-> b n d", b=bs)[:, :, :3]
25+
return point
26+
27+
def __call__(self, data):
28+
d: Dict = dict(data)
29+
for key_label in self.label_names.keys():
30+
points = d.get(key_label, [])
31+
if len(points) < 1:
32+
continue
33+
reoriented_points = self.transform_points(
34+
np.array(points)[None],
35+
np.linalg.inv(d[self.ref_image].meta["affine"].numpy()) @ d[self.ref_image].meta["original_affine"],
36+
)
37+
d[key_label] = reoriented_points[0]
38+
return d

0 commit comments

Comments
 (0)