From a566dc69d19c7437a9b6b7dcd5dbfd7a9f077bab Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Fri, 23 Jul 2021 09:52:22 +0100 Subject: [PATCH] fixes randbiasfield (#2645) Signed-off-by: Wenqi Li --- monai/transforms/intensity/array.py | 33 +++++++++++------------------ tests/test_random_bias_field.py | 17 ++++++--------- tests/test_random_bias_fieldd.py | 18 +++++++--------- 3 files changed, 26 insertions(+), 42 deletions(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 52774f75db..65c114abcd 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -431,22 +431,19 @@ def __init__( self.coeff_range = coeff_range self.dtype = dtype - def _generate_random_field( - self, - spatial_shape: Tuple[int, ...], - rank: int, - degree: int, - coeff: Tuple[int, ...], - ): + self._coeff = [1.0] + + def _generate_random_field(self, spatial_shape: Sequence[int], degree: int, coeff: Sequence[float]): """ products of polynomials as bias field estimations """ + rank = len(spatial_shape) coeff_mat = np.zeros((degree + 1,) * rank) coords = [np.linspace(-1.0, 1.0, dim, dtype=np.float32) for dim in spatial_shape] if rank == 2: coeff_mat[np.tril_indices(degree + 1)] = coeff - field = np.polynomial.legendre.leggrid2d(coords[0], coords[1], coeff_mat) - elif rank == 3: + return np.polynomial.legendre.leggrid2d(coords[0], coords[1], coeff_mat) + if rank == 3: pts: List[List[int]] = [[0, 0, 0]] for i in range(degree + 1): for j in range(degree + 1 - i): @@ -456,16 +453,12 @@ def _generate_random_field( pts = pts[1:] np_pts = np.stack(pts) coeff_mat[np_pts[:, 0], np_pts[:, 1], np_pts[:, 2]] = coeff - field = np.polynomial.legendre.leggrid3d(coords[0], coords[1], coords[2], coeff_mat) - else: - raise NotImplementedError("only supports 2D or 3D fields") - return field + return np.polynomial.legendre.leggrid3d(coords[0], coords[1], coords[2], coeff_mat) + raise NotImplementedError("only supports 2D or 3D fields") def randomize(self, data: np.ndarray) -> None: super().randomize(None) - self.spatial_shape = data.shape[1:] - self.rank = len(self.spatial_shape) - n_coeff = int(np.prod([(self.degree + k) / k for k in range(1, self.rank + 1)])) + n_coeff = int(np.prod([(self.degree + k) / k for k in range(1, len(data.shape[1:]) + 1)])) self._coeff = self.R.uniform(*self.coeff_range, n_coeff).tolist() def __call__(self, img: np.ndarray): @@ -475,17 +468,15 @@ def __call__(self, img: np.ndarray): self.randomize(data=img) if not self._do_transform: return img - num_channels = img.shape[0] + num_channels, *spatial_shape = img.shape _bias_fields = np.stack( [ - self._generate_random_field( - spatial_shape=self.spatial_shape, rank=self.rank, degree=self.degree, coeff=self._coeff - ) + self._generate_random_field(spatial_shape=spatial_shape, degree=self.degree, coeff=self._coeff) for _ in range(num_channels) ], axis=0, ) - return (img * _bias_fields).astype(self.dtype) + return (img * np.exp(_bias_fields)).astype(self.dtype) class NormalizeIntensity(Transform): diff --git a/tests/test_random_bias_field.py b/tests/test_random_bias_field.py index 16b4ab6917..5aeeb79874 100644 --- a/tests/test_random_bias_field.py +++ b/tests/test_random_bias_field.py @@ -18,17 +18,12 @@ TEST_CASES_2D = [{}, (3, 32, 32)] TEST_CASES_3D = [{}, (3, 32, 32, 32)] -TEST_CASES_2D_ZERO_RANGE = [{"coeff_range": (0.0, 0.0)}, (3, 32, 32)] -TEST_CASES_2D_ONES = [{"coeff_range": (1.0, 1.0)}, np.asarray([[[2, -2], [2, 10]]])] +TEST_CASES_2D_ZERO_RANGE = [{"coeff_range": (0.0, 0.0)}, (2, 3, 3)] +TEST_CASES_2D_ONES = [{"coeff_range": (1.0, 1.0)}, np.asarray([[[7.389056, 0.1353353], [7.389056, 22026.46]]])] class TestRandBiasField(unittest.TestCase): - @parameterized.expand( - [ - TEST_CASES_2D, - TEST_CASES_3D, - ] - ) + @parameterized.expand([TEST_CASES_2D, TEST_CASES_3D]) def test_output_shape(self, class_args, img_shape): for degree in [1, 2, 3]: bias_field = RandBiasField(degree=degree, **class_args) @@ -44,16 +39,16 @@ def test_output_shape(self, class_args, img_shape): @parameterized.expand([TEST_CASES_2D_ZERO_RANGE]) def test_zero_range(self, class_args, img_shape): bias_field = RandBiasField(**class_args) - img = np.random.rand(*img_shape) + img = np.ones(img_shape) output = bias_field(img) - np.testing.assert_equal(output, np.zeros(img_shape)) + np.testing.assert_allclose(output, np.ones(img_shape), rtol=1e-3) @parameterized.expand([TEST_CASES_2D_ONES]) def test_one_range_input(self, class_args, expected): bias_field = RandBiasField(**class_args) img = np.ones([1, 2, 2]) output = bias_field(img) - np.testing.assert_equal(output, expected.astype(bias_field.dtype)) + np.testing.assert_allclose(output, expected.astype(bias_field.dtype), rtol=1e-3) def test_zero_prob(self): bias_field = RandBiasField(prob=0.0) diff --git a/tests/test_random_bias_fieldd.py b/tests/test_random_bias_fieldd.py index 136eb41f2e..aa2e206de9 100644 --- a/tests/test_random_bias_fieldd.py +++ b/tests/test_random_bias_fieldd.py @@ -19,16 +19,14 @@ TEST_CASES_2D = [{}, (3, 32, 32)] TEST_CASES_3D = [{}, (3, 32, 32, 32)] TEST_CASES_2D_ZERO_RANGE = [{"coeff_range": (0.0, 0.0)}, (3, 32, 32)] -TEST_CASES_2D_ONES = [{"coeff_range": (1.0, 1.0)}, np.asarray([[[2, -2], [2, 10]]])] +TEST_CASES_2D_ONES = [ + {"coeff_range": (1.0, 1.0)}, + np.asarray([[[7.3890562e00, 1.3533528e-01], [7.3890562e00, 2.2026465e04]]]), +] class TestRandBiasFieldd(unittest.TestCase): - @parameterized.expand( - [ - TEST_CASES_2D, - TEST_CASES_3D, - ] - ) + @parameterized.expand([TEST_CASES_2D, TEST_CASES_3D]) def test_output_shape(self, class_args, img_shape): key = "img" bias_field = RandBiasFieldd(keys=[key], **class_args) @@ -41,9 +39,9 @@ def test_output_shape(self, class_args, img_shape): def test_zero_range(self, class_args, img_shape): key = "img" bias_field = RandBiasFieldd(keys=[key], **class_args) - img = np.random.rand(*img_shape) + img = np.ones(img_shape) output = bias_field({key: img}) - np.testing.assert_equal(output[key], np.zeros(img_shape)) + np.testing.assert_allclose(output[key], np.ones(img_shape)) @parameterized.expand([TEST_CASES_2D_ONES]) def test_one_range_input(self, class_args, expected): @@ -51,7 +49,7 @@ def test_one_range_input(self, class_args, expected): bias_field = RandBiasFieldd(keys=[key], **class_args) img = np.ones([1, 2, 2]) output = bias_field({key: img}) - np.testing.assert_equal(output[key], expected.astype(bias_field.rand_bias_field.dtype)) + np.testing.assert_allclose(output[key], expected.astype(bias_field.rand_bias_field.dtype), rtol=1e-3) def test_zero_prob(self): key = "img"