Skip to content

Commit 9b54616

Browse files
dongyang0122dongyDong Yangpre-commit-ci[bot]
authored
Update detection visualization tutorial (#768)
* init vis folder Signed-off-by: dongy <[email protected]> * update repo Signed-off-by: Dong Yang <[email protected]> * update repo Signed-off-by: Dong Yang <[email protected]> * update repo Signed-off-by: Dong Yang <[email protected]> * update repo Signed-off-by: Dong Yang <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update repo Signed-off-by: dongy <[email protected]> * update repo Signed-off-by: Dong Yang <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update repo Signed-off-by: Dong Yang <[email protected]> * update repo Signed-off-by: dongy <[email protected]> Co-authored-by: dongy <[email protected]> Co-authored-by: Dong Yang <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 2da31b6 commit 9b54616

File tree

4 files changed

+139
-16
lines changed

4 files changed

+139
-16
lines changed

detection/luna16_visualization/README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Visualizing box prediction/annotation in 3D medical image detection is not strai
55
## Prerequisite
66

77
- The Version of 3D Slicer should be **4.11.20210226** or later.
8-
- Box information should be stored in a ".json" file. The "data\_sample.json" file is an example. The information of *N* boxes is stored under the key "box" as *N* lists. The six values are the *X*/*Y*/*Z* coordinates of the box center and the box length in the *X*/*Y*/*Z* axes. All the coordinate values are in the world coordinate system.
8+
- Box information should be stored in a ".json" file. The information of *N* boxes is stored under the key "box" as *N* lists. The six values could be in different 3D [modes](https://github.com/Project-MONAI/MONAI/blob/edf3b742a4ae85d1f30462ed0c7511c520fae888/monai/data/box_utils.py#L447-L456) (e.g., corner or center coordinates). All the coordinate values are in either world coordinate system or image coordinate system. The "data\_sample.json" file is an example with X/Y/Z-axial center coordinates and box lengths (box mode **cccwhd**).
99

1010
```
1111
"box": [
@@ -32,13 +32,30 @@ Visualizing box prediction/annotation in 3D medical image detection is not strai
3232

3333
### 1. Create ".obj" file for predictions/annotation using the "save\_obj.sh" script.
3434

35-
```
36-
#!/bin/bash
35+
User needs to specify the box mode and whether to use image coordinates. If image coordinates are specified, the image root directory needs to be provided.
36+
37+
#### Example of boxes in world coordinate
3738

39+
```
3840
INPUT_DATASET_JSON="./data_sample.json"
3941
OUTPUT_DIR="./out"
4042
41-
python save_obj.py --input_dataset_json ${INPUT_DATASET_JSON} \
43+
python save_obj.py --input_box_mode "cccwhd" \
44+
--input_dataset_json ${INPUT_DATASET_JSON} \
45+
--output_dir ${OUTPUT_DIR}
46+
```
47+
48+
#### Example of boxes in image coordinate
49+
50+
```
51+
IMAGE_DATA_ROOT="/data_root"
52+
INPUT_DATASET_JSON="./data_sample_xyzxyz_image-coordinate.json"
53+
OUTPUT_DIR="./out_image_coord"
54+
55+
python save_obj.py --image_coordinate \
56+
--image_data_root ${IMAGE_DATA_ROOT} \
57+
--input_box_mode "xyzxyz" \
58+
--input_dataset_json ${INPUT_DATASET_JSON} \
4259
--output_dir ${OUTPUT_DIR}
4360
```
4461

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"validation": [
3+
{
4+
"image": "1.3.6.1.4.1.14519.5.2.1.6279.6001.108197895896446896160048741492.mhd",
5+
"box": [
6+
[
7+
106,
8+
342.25,
9+
31.453125,
10+
114.75,
11+
351,
12+
34.0625
13+
]
14+
],
15+
"label": [
16+
0
17+
]
18+
},
19+
{
20+
"image": "1.3.6.1.4.1.14519.5.2.1.6279.6001.109002525524522225658609808059.mhd",
21+
"box": [
22+
[
23+
413.75,
24+
273.25,
25+
62.90625,
26+
438.5,
27+
298,
28+
73.8125
29+
],
30+
[
31+
404.5,
32+
333.25,
33+
54.8125,
34+
412.25,
35+
341.25,
36+
58.3125
37+
]
38+
],
39+
"label": [
40+
0,
41+
0
42+
]
43+
}
44+
]
45+
}

detection/luna16_visualization/save_obj.py

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
import argparse
1515
import csv
1616
import json
17+
import monai
1718
import nibabel as nib
1819
import numpy as np
1920
import os
2021

22+
from monai.data.box_utils import convert_box_mode
23+
2124

2225
def save_obj(vertices, faces, filename):
2326

@@ -29,20 +32,44 @@ def save_obj(vertices, faces, filename):
2932
for t in faces:
3033
f.write("f {} {} {} {}\n".format(*(np.array(t) + 1)))
3134

35+
return
36+
3237

3338
def main():
34-
parser = argparse.ArgumentParser()
39+
parser = argparse.ArgumentParser(
40+
description="Save .obj files of boxes for visualization using 3D Slicer.",
41+
)
42+
parser.add_argument(
43+
"--image_coordinate",
44+
action="store_true",
45+
help="if box coordinates in image coordinate",
46+
)
47+
parser.add_argument(
48+
"--image_data_root",
49+
type=str,
50+
default="",
51+
help="image data root",
52+
)
53+
parser.add_argument(
54+
"--input_box_mode",
55+
action="store",
56+
type=str,
57+
required=True,
58+
help="input box coordinate mode",
59+
),
3560
parser.add_argument(
3661
"--input_dataset_json",
3762
action="store",
3863
type=str,
3964
required=True,
65+
help="the dataset .json with box information",
4066
)
4167
parser.add_argument(
4268
"--output_dir",
4369
action="store",
4470
type=str,
4571
required=True,
72+
help="output directory",
4673
)
4774
args = parser.parse_args()
4875

@@ -52,6 +79,9 @@ def main():
5279
with open(os.path.join(args.input_dataset_json)) as f:
5380
input_dataset = json.load(f)
5481

82+
if args.image_coordinate:
83+
image_loader = monai.transforms.LoadImage(reader=None, image_only=False)
84+
5585
for key in input_dataset.keys():
5686
section = input_dataset[key]
5787

@@ -61,17 +91,36 @@ def main():
6191
box_filename = box_filename.split(os.sep)[-1]
6292
print("-- {0:d}th case name:".format(_k + 1), box_filename)
6393

94+
if args.image_coordinate:
95+
image_name = os.path.join(args.image_data_root, section[_k]["image"])
96+
image_data = image_loader(image_name)
97+
affine = image_data[1]["original_affine"]
98+
99+
# convert to RAS coordinate system (required by 3D Slicer)
100+
for _i in range(3):
101+
if affine[_i, _i] < 0:
102+
affine[_i, _i] *= -1.0
103+
affine[_i, 3] *= -1.0
104+
64105
vertices = []
65106
faces = []
66107
_i = 0
67-
for vec in box_data:
68-
xmin = vec[0] - 0.5 * vec[3]
69-
ymin = vec[1] - 0.5 * vec[4]
70-
zmin = vec[2] - 0.5 * vec[5]
71-
72-
xmax = vec[0] + 0.5 * vec[3]
73-
ymax = vec[1] + 0.5 * vec[4]
74-
zmax = vec[2] + 0.5 * vec[5]
108+
for _vec in box_data:
109+
vec = convert_box_mode(
110+
np.expand_dims(np.array(_vec), axis=0),
111+
src_mode=args.input_box_mode,
112+
dst_mode="xyzxyz",
113+
)
114+
vec = vec.squeeze()
115+
xmin, ymin, zmin = vec[0], vec[1], vec[2]
116+
xmax, ymax, zmax = vec[3], vec[4], vec[5]
117+
118+
if args.image_coordinate:
119+
_out = affine @ np.transpose(np.array([xmin, ymin, zmin, 1]))
120+
xmin, ymin, zmin = _out[0], _out[1], _out[2]
121+
122+
_out = affine @ np.transpose(np.array([xmax, ymax, zmax, 1]))
123+
xmax, ymax, zmax = _out[0], _out[1], _out[2]
75124

76125
vertices += [
77126
(xmax, ymax, zmin),
@@ -95,7 +144,9 @@ def main():
95144

96145
_i += 1
97146

98-
save_obj(vertices, faces, os.path.join(args.output_dir, box_filename + ".obj"))
147+
save_obj(
148+
vertices, faces, os.path.join(args.output_dir, box_filename + ".obj")
149+
)
99150

100151
return
101152

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
#!/bin/bash
22

33
INPUT_DATASET_JSON="./data_sample.json"
4-
OUTPUT_DIR="./out"
4+
OUTPUT_DIR="./out_world_coord"
55

6-
python save_obj.py --input_dataset_json ${INPUT_DATASET_JSON} \
6+
python save_obj.py --input_box_mode "cccwhd" \
7+
--input_dataset_json ${INPUT_DATASET_JSON} \
78
--output_dir ${OUTPUT_DIR}
9+
10+
IMAGE_DATA_ROOT="/data_root"
11+
INPUT_DATASET_JSON="./data_sample_xyzxyz_image-coordinate.json"
12+
OUTPUT_DIR="./out_image_coord"
13+
14+
python save_obj.py --image_coordinate \
15+
--image_data_root ${IMAGE_DATA_ROOT} \
16+
--input_box_mode "xyzxyz" \
17+
--input_dataset_json ${INPUT_DATASET_JSON} \

0 commit comments

Comments
 (0)