From d1829f3f70771b4ca18d8cd44ac01ef18b1f3c45 Mon Sep 17 00:00:00 2001 From: jmnolte Date: Fri, 7 Jul 2023 10:08:46 +0200 Subject: [PATCH 01/13] Add random sorting option to sort_fn argument in GridPatch transform --- monai/transforms/spatial/array.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index ec38dfaffe..04eff1d6a1 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3262,7 +3262,7 @@ def filter_threshold(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: """ - Sort the patches based on the sum of their intensity, and just keep `self.num_patches` of them. + Sort the patches based on the sum of their intensity or in random order, and just keep `self.num_patches` of them. Args: image_np: a numpy.ndarray or torch.Tensor representing a stack of patches. @@ -3277,8 +3277,10 @@ def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tupl idx = argsort(image_np.sum(tuple(range(1, n_dims)))) elif self.sort_fn == GridPatchSort.MAX: idx = argsort(-image_np.sum(tuple(range(1, n_dims)))) + elif self.sort_fn == GridPatchSort.RANDOM: + idx = np.random.permutation(image_np.shape[0]) else: - raise ValueError(f'`sort_fn` should be either "min", "max" or None! {self.sort_fn} provided!') + raise ValueError(f'`sort_fn` should be either "min", "max", "random" or None! {self.sort_fn} provided!') idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] image_np = image_np[idx] From c2f413712adcc9737661df06d40c60e7817e3698 Mon Sep 17 00:00:00 2001 From: jmnolte Date: Fri, 7 Jul 2023 10:08:46 +0200 Subject: [PATCH 02/13] Add random sorting option to sort_fn argument in GridPatch transform Signed-off-by: jmnolte --- monai/transforms/spatial/array.py | 6 ++++-- tests/test_grid_patch.py | 4 ++++ tests/test_grid_patchd.py | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index ec38dfaffe..04eff1d6a1 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3262,7 +3262,7 @@ def filter_threshold(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: """ - Sort the patches based on the sum of their intensity, and just keep `self.num_patches` of them. + Sort the patches based on the sum of their intensity or in random order, and just keep `self.num_patches` of them. Args: image_np: a numpy.ndarray or torch.Tensor representing a stack of patches. @@ -3277,8 +3277,10 @@ def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tupl idx = argsort(image_np.sum(tuple(range(1, n_dims)))) elif self.sort_fn == GridPatchSort.MAX: idx = argsort(-image_np.sum(tuple(range(1, n_dims)))) + elif self.sort_fn == GridPatchSort.RANDOM: + idx = np.random.permutation(image_np.shape[0]) else: - raise ValueError(f'`sort_fn` should be either "min", "max" or None! {self.sort_fn} provided!') + raise ValueError(f'`sort_fn` should be either "min", "max", "random" or None! {self.sort_fn} provided!') idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] image_np = image_np[idx] diff --git a/tests/test_grid_patch.py b/tests/test_grid_patch.py index cd1c5b6531..76e3395923 100644 --- a/tests/test_grid_patch.py +++ b/tests/test_grid_patch.py @@ -55,6 +55,8 @@ TEST_CASE_15 = [{"patch_size": (2, 2), "num_patches": 3, "threshold": 50.0}, A, [A11]] # threshold filtering with num_patches less than available patches (count filtering) TEST_CASE_16 = [{"patch_size": (2, 2), "num_patches": 2, "threshold": 150.0}, A, [A11, A12]] +# random sorting of output patches +TEST_CASE_17 = [{"patch_size": (2, 2), "num_patches": 4, "sort_fn": "random"}, A, [A22, A11, A12, A21]] TEST_CASE_META_0 = [ {"patch_size": (2, 2)}, @@ -94,6 +96,7 @@ TEST_CASES.append([p, *TEST_CASE_14]) TEST_CASES.append([p, *TEST_CASE_15]) TEST_CASES.append([p, *TEST_CASE_16]) + TEST_CASES.append([p, *TEST_CASE_17]) class TestGridPatch(unittest.TestCase): @@ -105,6 +108,7 @@ def test_grid_patch(self, in_type, input_parameters, image, expected): output = splitter(input_image) self.assertEqual(len(output), len(expected)) for output_patch, expected_patch in zip(output, expected): + np.random.seed(123) assert_allclose( output_patch, in_type(expected_patch), diff --git a/tests/test_grid_patchd.py b/tests/test_grid_patchd.py index 4f317e4677..0e8b21463b 100644 --- a/tests/test_grid_patchd.py +++ b/tests/test_grid_patchd.py @@ -54,6 +54,8 @@ TEST_CASE_15 = [{"patch_size": (2, 2), "threshold": 50.0, "num_patches": 3}, {"image": A}, [A11]] # threshold filtering with num_patches less than available patches (count filtering) TEST_CASE_16 = [{"patch_size": (2, 2), "threshold": 150.0, "num_patches": 2}, {"image": A}, [A11, A12]] +# random sorting of output patches +TEST_CASE_17 = [{"patch_size": (2, 2), "num_patches": 4, "sort_fn": "random"}, {"image": A}, [A22, A11, A12, A21]] TEST_SINGLE = [] for p in TEST_NDARRAYS: @@ -74,6 +76,7 @@ TEST_SINGLE.append([p, *TEST_CASE_14]) TEST_SINGLE.append([p, *TEST_CASE_15]) TEST_SINGLE.append([p, *TEST_CASE_16]) + TEST_SINGLE.append([p, *TEST_CASE_17]) class TestGridPatchd(unittest.TestCase): @@ -90,6 +93,7 @@ def test_grid_patchd(self, in_type, input_parameters, image_dict, expected): output = splitter(input_dict) self.assertEqual(len(output[image_key]), len(expected)) for output_patch, expected_patch in zip(output[image_key], expected): + np.random.seed(123) assert_allclose( output_patch, in_type(expected_patch), From 4ab607054ef84c97fc3370071d8df08ff42562d4 Mon Sep 17 00:00:00 2001 From: jmnolte Date: Fri, 7 Jul 2023 14:56:23 +0200 Subject: [PATCH 03/13] DCO Remediation Commit for jmnolte I, jmnolte , hereby add my Signed-off-by to this commit: d1829f3f70771b4ca18d8cd44ac01ef18b1f3c45 Signed-off-by: jmnolte From 044a8cfa275bef7f8c6efa98f87abc73cbf62a5e Mon Sep 17 00:00:00 2001 From: jmnolte Date: Mon, 10 Jul 2023 14:49:27 +0200 Subject: [PATCH 04/13] Refactor random selection to ensure reproducible random states Signed-off-by: jmnolte --- monai/transforms/spatial/array.py | 20 ++++++++++++++------ tests/test_grid_patch.py | 4 ---- tests/test_grid_patchd.py | 4 ---- tests/test_rand_grid_patch.py | 9 +++++++++ tests/test_rand_grid_patchd.py | 10 ++++++++++ 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 04eff1d6a1..458f51989f 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3243,6 +3243,7 @@ def __init__( self.num_patches = num_patches self.sort_fn = sort_fn.lower() if sort_fn else None self.threshold = threshold + self.patch_idx = tuple(range(num_patches)) if num_patches else None def filter_threshold(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: """ @@ -3278,9 +3279,9 @@ def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tupl elif self.sort_fn == GridPatchSort.MAX: idx = argsort(-image_np.sum(tuple(range(1, n_dims)))) elif self.sort_fn == GridPatchSort.RANDOM: - idx = np.random.permutation(image_np.shape[0]) + idx = self.patch_idx else: - raise ValueError(f'`sort_fn` should be either "min", "max", "random" or None! {self.sort_fn} provided!') + raise ValueError(f'`sort_fn` should be either "min", "max", or None! {self.sort_fn} provided!') idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] image_np = image_np[idx] @@ -3373,7 +3374,7 @@ class RandGridPatch(GridPatch, RandomizableTransform, MultiSampleTrait): overlap: the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). If only one float number is given, it will be applied to all dimensions. Defaults to 0.0. sort_fn: when `num_patches` is provided, it determines if keep patches with highest values (`"max"`), - lowest values (`"min"`), or in their default order (`None`). Default to None. + lowest values (`"min"`), in random ("random"), or in their default order (`None`). Default to None. threshold: a value to keep only the patches whose sum of intensities are less than the threshold. Defaults to no filtering. pad_mode: the mode for padding the input image by `patch_size` to include patches that cross boundaries. @@ -3416,7 +3417,7 @@ def __init__( super().__init__( patch_size=patch_size, offset=(), - num_patches=num_patches, + num_patches=None, overlap=overlap, sort_fn=sort_fn, threshold=threshold, @@ -3425,7 +3426,9 @@ def __init__( ) self.min_offset = min_offset self.max_offset = max_offset - + self.num_patches = num_patches + self.sort_fn = sort_fn + def randomize(self, array): if self.min_offset is None: min_offset = (0,) * len(self.patch_size) @@ -3438,7 +3441,12 @@ def randomize(self, array): self.offset = tuple(self.R.randint(low=low, high=high + 1) for low, high in zip(min_offset, max_offset)) + if self.sort_fn == GridPatchSort.RANDOM: + self.patch_idx = self.R.permutation(tuple(range(self.num_patches))) + elif self.sort_fn not in (None, GridPatchSort.MIN, GridPatchSort.MAX): + raise ValueError(f'`sort_fn` should be either "min", "max", or None! {self.sort_fn} provided!') + def __call__(self, array: NdarrayOrTensor, randomize: bool = True): if randomize: self.randomize(array) - return super().__call__(array) + return super().__call__(array) \ No newline at end of file diff --git a/tests/test_grid_patch.py b/tests/test_grid_patch.py index 76e3395923..cd1c5b6531 100644 --- a/tests/test_grid_patch.py +++ b/tests/test_grid_patch.py @@ -55,8 +55,6 @@ TEST_CASE_15 = [{"patch_size": (2, 2), "num_patches": 3, "threshold": 50.0}, A, [A11]] # threshold filtering with num_patches less than available patches (count filtering) TEST_CASE_16 = [{"patch_size": (2, 2), "num_patches": 2, "threshold": 150.0}, A, [A11, A12]] -# random sorting of output patches -TEST_CASE_17 = [{"patch_size": (2, 2), "num_patches": 4, "sort_fn": "random"}, A, [A22, A11, A12, A21]] TEST_CASE_META_0 = [ {"patch_size": (2, 2)}, @@ -96,7 +94,6 @@ TEST_CASES.append([p, *TEST_CASE_14]) TEST_CASES.append([p, *TEST_CASE_15]) TEST_CASES.append([p, *TEST_CASE_16]) - TEST_CASES.append([p, *TEST_CASE_17]) class TestGridPatch(unittest.TestCase): @@ -108,7 +105,6 @@ def test_grid_patch(self, in_type, input_parameters, image, expected): output = splitter(input_image) self.assertEqual(len(output), len(expected)) for output_patch, expected_patch in zip(output, expected): - np.random.seed(123) assert_allclose( output_patch, in_type(expected_patch), diff --git a/tests/test_grid_patchd.py b/tests/test_grid_patchd.py index 0e8b21463b..4f317e4677 100644 --- a/tests/test_grid_patchd.py +++ b/tests/test_grid_patchd.py @@ -54,8 +54,6 @@ TEST_CASE_15 = [{"patch_size": (2, 2), "threshold": 50.0, "num_patches": 3}, {"image": A}, [A11]] # threshold filtering with num_patches less than available patches (count filtering) TEST_CASE_16 = [{"patch_size": (2, 2), "threshold": 150.0, "num_patches": 2}, {"image": A}, [A11, A12]] -# random sorting of output patches -TEST_CASE_17 = [{"patch_size": (2, 2), "num_patches": 4, "sort_fn": "random"}, {"image": A}, [A22, A11, A12, A21]] TEST_SINGLE = [] for p in TEST_NDARRAYS: @@ -76,7 +74,6 @@ TEST_SINGLE.append([p, *TEST_CASE_14]) TEST_SINGLE.append([p, *TEST_CASE_15]) TEST_SINGLE.append([p, *TEST_CASE_16]) - TEST_SINGLE.append([p, *TEST_CASE_17]) class TestGridPatchd(unittest.TestCase): @@ -93,7 +90,6 @@ def test_grid_patchd(self, in_type, input_parameters, image_dict, expected): output = splitter(input_dict) self.assertEqual(len(output[image_key]), len(expected)) for output_patch, expected_patch in zip(output[image_key], expected): - np.random.seed(123) assert_allclose( output_patch, in_type(expected_patch), diff --git a/tests/test_rand_grid_patch.py b/tests/test_rand_grid_patch.py index a118a0f420..fb48ea7a97 100644 --- a/tests/test_rand_grid_patch.py +++ b/tests/test_rand_grid_patch.py @@ -59,6 +59,12 @@ [np.pad(A[:, :2, 1:], ((0, 0), (1, 0), (0, 0)), mode="constant", constant_values=255)], ] TEST_CASE_10 = [{"patch_size": (2, 2), "min_offset": 0, "max_offset": 0, "threshold": 50.0}, A, [A11]] +TEST_CASE_11 = [{"patch_size": (2, 2), "sort_fn": "random", "num_patches": 2}, A, [A11, A12]] +TEST_CASE_12 = [{"patch_size": (2, 2), "sort_fn": "random", "num_patches": 4}, A, [A11, A12, A21, A22]] +TEST_CASE_13 = [ + {"patch_size": (2, 2), "min_offset": 0, "max_offset": 1, "num_patches": 1, "sort_fn": "random"}, + A, [A[:, 1:3, 1:3]], +] TEST_CASE_META_0 = [ {"patch_size": (2, 2)}, @@ -92,6 +98,9 @@ TEST_SINGLE.append([p, *TEST_CASE_8]) TEST_SINGLE.append([p, *TEST_CASE_9]) TEST_SINGLE.append([p, *TEST_CASE_10]) + TEST_SINGLE.append([p, *TEST_CASE_11]) + TEST_SINGLE.append([p, *TEST_CASE_12]) + TEST_SINGLE.append([p, *TEST_CASE_13]) class TestRandGridPatch(unittest.TestCase): diff --git a/tests/test_rand_grid_patchd.py b/tests/test_rand_grid_patchd.py index e93a6f6cd1..23ca4a7881 100644 --- a/tests/test_rand_grid_patchd.py +++ b/tests/test_rand_grid_patchd.py @@ -58,6 +58,13 @@ [np.pad(A[:, :2, 1:], ((0, 0), (1, 0), (0, 0)), mode="constant", constant_values=255)], ] TEST_CASE_10 = [{"patch_size": (2, 2), "min_offset": 0, "max_offset": 0, "threshold": 50.0}, {"image": A}, [A11]] +TEST_CASE_11 = [{"patch_size": (2, 2), "sort_fn": "random", "num_patches": 2}, {"image": A}, [A11, A12]] +TEST_CASE_12 = [{"patch_size": (2, 2), "sort_fn": "random", "num_patches": 4}, {"image": A}, [A11, A12, A21, A22]] +TEST_CASE_13 = [ + {"patch_size": (2, 2), "min_offset": 0, "max_offset": 1, "num_patches": 1, "sort_fn": "random"}, + {"image": A}, + [A[:, 1:3, 1:3]], +] TEST_SINGLE = [] for p in TEST_NDARRAYS: @@ -72,6 +79,9 @@ TEST_SINGLE.append([p, *TEST_CASE_8]) TEST_SINGLE.append([p, *TEST_CASE_9]) TEST_SINGLE.append([p, *TEST_CASE_10]) + TEST_SINGLE.append([p, *TEST_CASE_11]) + TEST_SINGLE.append([p, *TEST_CASE_12]) + TEST_SINGLE.append([p, *TEST_CASE_13]) class TestRandGridPatchd(unittest.TestCase): From 5b9d5ef303169bc0a6a8d83e1de0d139a740af83 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 12:56:17 +0000 Subject: [PATCH 05/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/spatial/array.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 458f51989f..4095e61318 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3428,7 +3428,7 @@ def __init__( self.max_offset = max_offset self.num_patches = num_patches self.sort_fn = sort_fn - + def randomize(self, array): if self.min_offset is None: min_offset = (0,) * len(self.patch_size) @@ -3449,4 +3449,4 @@ def randomize(self, array): def __call__(self, array: NdarrayOrTensor, randomize: bool = True): if randomize: self.randomize(array) - return super().__call__(array) \ No newline at end of file + return super().__call__(array) From 6ac917544d8e474326badad3604df541593169da Mon Sep 17 00:00:00 2001 From: jmnolte Date: Mon, 10 Jul 2023 15:02:04 +0200 Subject: [PATCH 06/13] Fix num_patches in init of RandGridPatch class Signed-off-by: jmnolte --- monai/transforms/spatial/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 458f51989f..721daccbef 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3417,7 +3417,7 @@ def __init__( super().__init__( patch_size=patch_size, offset=(), - num_patches=None, + num_patches=num_patches, overlap=overlap, sort_fn=sort_fn, threshold=threshold, From 3cd7566ebe14830f6c4c8a5fc7d0f45fa47b5102 Mon Sep 17 00:00:00 2001 From: jmnolte Date: Wed, 12 Jul 2023 10:31:16 +0200 Subject: [PATCH 07/13] 6699 Override parent filter count method in parent class Signed-off-by: jmnolte --- monai/transforms/spatial/array.py | 14 ++++++++------ tests/test_rand_grid_patch.py | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index da3b50f738..046d48aa7f 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3243,7 +3243,6 @@ def __init__( self.num_patches = num_patches self.sort_fn = sort_fn.lower() if sort_fn else None self.threshold = threshold - self.patch_idx = tuple(range(num_patches)) if num_patches else None def filter_threshold(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: """ @@ -3278,8 +3277,6 @@ def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tupl idx = argsort(image_np.sum(tuple(range(1, n_dims)))) elif self.sort_fn == GridPatchSort.MAX: idx = argsort(-image_np.sum(tuple(range(1, n_dims)))) - elif self.sort_fn == GridPatchSort.RANDOM: - idx = self.patch_idx else: raise ValueError(f'`sort_fn` should be either "min", "max", or None! {self.sort_fn} provided!') idx = idx[: self.num_patches] @@ -3440,11 +3437,16 @@ def randomize(self, array): max_offset = ensure_tuple_rep(self.max_offset, len(self.patch_size)) self.offset = tuple(self.R.randint(low=low, high=high + 1) for low, high in zip(min_offset, max_offset)) - + + def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: if self.sort_fn == GridPatchSort.RANDOM: - self.patch_idx = self.R.permutation(tuple(range(self.num_patches))) + idx = self.R.permutation(tuple(range(self.num_patches))) + idx = idx[: self.num_patches] + idx_np = convert_data_type(idx, np.ndarray)[0] + return image_np[idx], locations[idx_np] elif self.sort_fn not in (None, GridPatchSort.MIN, GridPatchSort.MAX): - raise ValueError(f'`sort_fn` should be either "min", "max", or None! {self.sort_fn} provided!') + raise ValueError(f'`sort_fn` should be either "min", "max", "random" or None! {self.sort_fn} provided!') + return super().filter_count(image_np, locations) def __call__(self, array: NdarrayOrTensor, randomize: bool = True): if randomize: diff --git a/tests/test_rand_grid_patch.py b/tests/test_rand_grid_patch.py index fb48ea7a97..78164acc24 100644 --- a/tests/test_rand_grid_patch.py +++ b/tests/test_rand_grid_patch.py @@ -63,7 +63,8 @@ TEST_CASE_12 = [{"patch_size": (2, 2), "sort_fn": "random", "num_patches": 4}, A, [A11, A12, A21, A22]] TEST_CASE_13 = [ {"patch_size": (2, 2), "min_offset": 0, "max_offset": 1, "num_patches": 1, "sort_fn": "random"}, - A, [A[:, 1:3, 1:3]], + A, + [A[:, 1:3, 1:3]], ] TEST_CASE_META_0 = [ From bcdb1a61574f3ea34790e87d0efe4b60063d9c96 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Jul 2023 08:32:58 +0000 Subject: [PATCH 08/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/transforms/spatial/array.py | 2 +- tests/test_rand_grid_patch.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 046d48aa7f..8a66be269f 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3437,7 +3437,7 @@ def randomize(self, array): max_offset = ensure_tuple_rep(self.max_offset, len(self.patch_size)) self.offset = tuple(self.R.randint(low=low, high=high + 1) for low, high in zip(min_offset, max_offset)) - + def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: if self.sort_fn == GridPatchSort.RANDOM: idx = self.R.permutation(tuple(range(self.num_patches))) diff --git a/tests/test_rand_grid_patch.py b/tests/test_rand_grid_patch.py index 78164acc24..494330584a 100644 --- a/tests/test_rand_grid_patch.py +++ b/tests/test_rand_grid_patch.py @@ -63,7 +63,7 @@ TEST_CASE_12 = [{"patch_size": (2, 2), "sort_fn": "random", "num_patches": 4}, A, [A11, A12, A21, A22]] TEST_CASE_13 = [ {"patch_size": (2, 2), "min_offset": 0, "max_offset": 1, "num_patches": 1, "sort_fn": "random"}, - A, + A, [A[:, 1:3, 1:3]], ] From ab5d8496274c80a34aa263ad36b49926bd9bf263 Mon Sep 17 00:00:00 2001 From: Jakob Nolte <94493465+jmnolte@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:00:55 +0200 Subject: [PATCH 09/13] Update monai/transforms/spatial/array.py Co-authored-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> Signed-off-by: Jakob Nolte <94493465+jmnolte@users.noreply.github.com> --- monai/transforms/spatial/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 8a66be269f..7a9d937d16 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3262,7 +3262,7 @@ def filter_threshold(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: """ - Sort the patches based on the sum of their intensity or in random order, and just keep `self.num_patches` of them. + Sort the patches based on the sum of their intensity, and just keep `self.num_patches` of them. Args: image_np: a numpy.ndarray or torch.Tensor representing a stack of patches. From 95745e8920d8928278df46e97f10e5b6d1b9af8f Mon Sep 17 00:00:00 2001 From: Jakob Nolte <94493465+jmnolte@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:01:05 +0200 Subject: [PATCH 10/13] Update monai/transforms/spatial/array.py Co-authored-by: YunLiu <55491388+KumoLiu@users.noreply.github.com> Signed-off-by: Jakob Nolte <94493465+jmnolte@users.noreply.github.com> --- monai/transforms/spatial/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 7a9d937d16..f334e8b35d 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3440,7 +3440,7 @@ def randomize(self, array): def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: if self.sort_fn == GridPatchSort.RANDOM: - idx = self.R.permutation(tuple(range(self.num_patches))) + idx = self.R.permutation(tuple(range(image_np.shape[0])))) idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] return image_np[idx], locations[idx_np] From 8675f576658f7e74f6a0f6dc5305d5bf09c55478 Mon Sep 17 00:00:00 2001 From: jmnolte Date: Wed, 12 Jul 2023 15:47:11 +0200 Subject: [PATCH 11/13] 6699 Fix type error Signed-off-by: jmnolte --- monai/transforms/spatial/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index f334e8b35d..88887a24c8 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3440,7 +3440,7 @@ def randomize(self, array): def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: if self.sort_fn == GridPatchSort.RANDOM: - idx = self.R.permutation(tuple(range(image_np.shape[0])))) + idx = self.R.permutation(tuple(range(image_np.shape[0]))) idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] return image_np[idx], locations[idx_np] From c4e780ba0d8a011f1de7d0fe74f9cabd1c56545b Mon Sep 17 00:00:00 2001 From: jmnolte Date: Thu, 13 Jul 2023 09:10:45 +0200 Subject: [PATCH 12/13] 6699 Update RandGridPatchd docstring and fix typing error in RandGridPatch Signed-off-by: jmnolte --- monai/transforms/spatial/array.py | 4 +++- monai/transforms/spatial/dictionary.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 88887a24c8..3ee9b2fa44 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3443,7 +3443,9 @@ def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tupl idx = self.R.permutation(tuple(range(image_np.shape[0]))) idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] - return image_np[idx], locations[idx_np] + image_np = image_np[idx] + locations = locations[idx_np] + return image_np, locations elif self.sort_fn not in (None, GridPatchSort.MIN, GridPatchSort.MAX): raise ValueError(f'`sort_fn` should be either "min", "max", "random" or None! {self.sort_fn} provided!') return super().filter_count(image_np, locations) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 4ba5849c46..79742f0582 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -2436,7 +2436,7 @@ class RandGridPatchd(RandomizableTransform, MapTransform, MultiSampleTrait): overlap: the amount of overlap of neighboring patches in each dimension (a value between 0.0 and 1.0). If only one float number is given, it will be applied to all dimensions. Defaults to 0.0. sort_fn: when `num_patches` is provided, it determines if keep patches with highest values (`"max"`), - lowest values (`"min"`), or in their default order (`None`). Default to None. + lowest values (`"min"`), in random ("random"), or in their default order (`None`). Default to None. threshold: a value to keep only the patches whose sum of intensities are less than the threshold. Defaults to no filtering. pad_mode: the mode for padding the input image by `patch_size` to include patches that cross boundaries. From 65e34505f615b1258b6ae7474832f23a5814f136 Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 13 Jul 2023 10:03:52 +0100 Subject: [PATCH 13/13] type ignore idx type Signed-off-by: Wenqi Li --- monai/transforms/spatial/array.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 3ee9b2fa44..d37e69650c 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -3440,10 +3440,10 @@ def randomize(self, array): def filter_count(self, image_np: NdarrayOrTensor, locations: np.ndarray) -> tuple[NdarrayOrTensor, np.ndarray]: if self.sort_fn == GridPatchSort.RANDOM: - idx = self.R.permutation(tuple(range(image_np.shape[0]))) + idx = self.R.permutation(image_np.shape[0]) idx = idx[: self.num_patches] idx_np = convert_data_type(idx, np.ndarray)[0] - image_np = image_np[idx] + image_np = image_np[idx] # type: ignore locations = locations[idx_np] return image_np, locations elif self.sort_fn not in (None, GridPatchSort.MIN, GridPatchSort.MAX):