-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Cucim support for get_mask_edges
and get_surface_distance
#7008
Merged
wyli
merged 13 commits into
Project-MONAI:dev
from
john-zielke-snkeos:cucim_surface_distance_support
Oct 1, 2023
Merged
Cucim support for get_mask_edges
and get_surface_distance
#7008
wyli
merged 13 commits into
Project-MONAI:dev
from
john-zielke-snkeos:cucim_surface_distance_support
Oct 1, 2023
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Signed-off-by: John Zielke <john.zielke@snkeos.com>
I profiled this using the following code: from __future__ import annotations
import torch.utils.benchmark as benchmark
import torch
import cProfile
from monai.metrics.hausdorff_distance import compute_hausdorff_distance
from monai.metrics import utils
from pathlib import Path
import numpy as np
shapes = [
# (250, 250, 250),
# (100, 100, 100),
(50, 50, 50),
]
def create_batch(shape, device, use_random=False, random_ratio=0.2):
if not use_random:
[seg_1, seg_2, _] = [
create_spherical_seg_3d(radius=20, centre=(20, 20, 20), im_shape=shape),
create_spherical_seg_3d(radius=20, centre=(19, 19, 19), im_shape=shape),
None,
]
else:
[seg_1, seg_2, _] = [
create_random_seg(random_ratio, shape),
create_random_seg(random_ratio, shape),
None,
]
batch, n_class = 2, 3
batch_seg_1 = (
torch.tensor(seg_1)
.unsqueeze(0)
.unsqueeze(0)
.repeat([batch, n_class, 1, 1, 1])
.to(device)
)
batch_seg_2 = (
torch.tensor(seg_2)
.unsqueeze(0)
.unsqueeze(0)
.repeat([batch, n_class, 1, 1, 1])
.to(device)
)
return batch_seg_1, batch_seg_2
def benchmark_compare():
results = []
ratios = [0.2]
for shape in shapes:
for device in ["cpu", "cuda"]:
utils.has_cucim_distance_transform_edt = device != "cpu"
for use_random in [True, False]:
for ratio in ratios:
if not use_random and ratio != ratios[0]:
continue
batch_seg_1, batch_seg_2 = create_batch(
shape, device, use_random=use_random, random_ratio=ratio
)
t = benchmark.Timer(
stmt="compute_hausdorff_distance(y_pred=seg_1,y=seg_2)",
globals={
"seg_1": batch_seg_1,
"seg_2": batch_seg_2,
"compute_hausdorff_distance": compute_hausdorff_distance,
},
label=str(shape),
sub_label=f"random()>{ratio}"
if use_random
else "create_spherical_seg_3d",
description=device,
num_threads=1,
)
results.append(t.blocked_autorange(min_run_time=10))
compare = benchmark.Compare(results)
compare.print()
def profile():
Path("./perf").mkdir(exist_ok=True)
for shape in shapes:
for device in ["cpu", "cuda"]:
utils.has_cucim_distance_transform_edt = device != "cpu"
for use_random in [True, False]:
print(f"{shape} {device} {use_random}")
batch_seg_1, batch_seg_2 = create_batch(
shape, device, use_random=use_random
)
compute_hausdorff_distance(y_pred=batch_seg_1, y=batch_seg_2)
with cProfile.Profile() as pr:
compute_hausdorff_distance(y_pred=batch_seg_1, y=batch_seg_2)
pr.dump_stats(
f"./perf/hausdorff_{shape[0]}_{use_random}_{device}.prof"
)
def create_spherical_seg_3d(
radius: float = 20.0,
centre: tuple[int, int, int] = (49, 49, 49),
im_shape: tuple[int, int, int] = (99, 99, 99),
im_spacing: tuple[float, float, float] = (1.0, 1.0, 1.0),
) -> np.ndarray:
"""
Return a 3D image with a sphere inside. Voxel values will be
1 inside the sphere, and 0 elsewhere.
Args:
radius: radius of sphere (in terms of number of voxels, can be partial)
centre: location of sphere centre.
im_shape: shape of image to create.
im_spacing: spacing of image to create.
See also:
:py:meth:`~create_test_image_3d`
"""
# Create image
image = np.zeros(im_shape, dtype=np.int32)
spy, spx, spz = np.ogrid[: im_shape[0], : im_shape[1], : im_shape[2]]
spy = spy.astype(float) * im_spacing[0]
spx = spx.astype(float) * im_spacing[1]
spz = spz.astype(float) * im_spacing[2]
spy -= centre[0]
spx -= centre[1]
spz -= centre[2]
circle = (spx * spx + spy * spy + spz * spz) <= radius * radius
image[circle] = 1
image[~circle] = 0
return image
def create_random_seg(ratio, shape):
seg = np.zeros(shape, dtype=bool)
seg[np.random.random(shape) > ratio] = 1
return seg
if __name__ == "__main__":
benchmark_compare()
profile() |
Signed-off-by: John Zielke <john.zielke@snkeos.com>
7 tasks
Signed-off-by: John Zielke <john.zielke@snkeos.com>
Signed-off-by: John Zielke <john.zielke@snkeos.com>
Signed-off-by: John Zielke <john.zielke@snkeos.com>
Signed-off-by: John Zielke <john.zielke@snkeos.com>
Signed-off-by: John Zielke <john.zielke@snkeos.com>
Signed-off-by: John Zielke <john.zielke@snkeos.com>
/build |
/build |
/build |
wyli
approved these changes
Oct 1, 2023
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, it looks good to me.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
Add support for cucim for
get_mask_edges
andget_surface_distance
. This provides significant speedup in surface related metrics. Profiling on my system gave 3-20x speedups depending on the input shape:[---------------- (250, 250, 250) -----------------]
| cpu | cuda
1 threads: -----------------------------------------
random()>0.2 | 26400.8 | 1306.3
random()>0.5 | 26411.8 | 1399.1
random()>0.8 | 29993.2 | 1009.5
create_spherical_seg_3d | 623.8 | 45.0
Times are in milliseconds (ms).
[--------------- (100, 100, 100) ----------------]
| cpu | cuda
1 threads: ---------------------------------------
random()>0.2 | 1332.5 | 140.2
random()>0.5 | 1276.3 | 128.1
random()>0.8 | 1179.2 | 89.1
create_spherical_seg_3d | 111.7 | 44.0
Times are in milliseconds (ms).
[---------------- (50, 50, 50) ----------------]
| cpu | cuda
1 threads: -------------------------------------
random()>0.2 | 154.5 | 47.4
random()>0.5 | 166.7 | 39.3
random()>0.8 | 165.0 | 38.0
create_spherical_seg_3d | 77.2 | 44.4
Times are in milliseconds (ms).
where create_spherical_seg_3d uses the same function from test_hausdorff_distance, and binarizes random array using
random(shape)>ratio
.Types of changes
./runtests.sh -f -u --net --coverage
../runtests.sh --quick --unittests --disttests
.make html
command in thedocs/
folder.