Random bias field augmentation for MR images. The bias field is considered as a linear combination of smoothly varying basis (polynomial) functions, as described in `Automated Model-Based Tissue Classification of MR Images of the Brain <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp
| 744 | |
| 745 | |
| 746 | class RandBiasField(RandomizableTransform): |
| 747 | """ |
| 748 | Random bias field augmentation for MR images. |
| 749 | The bias field is considered as a linear combination of smoothly varying basis (polynomial) |
| 750 | functions, as described in `Automated Model-Based Tissue Classification of MR Images of the Brain |
| 751 | <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=811270>`_. |
| 752 | This implementation adapted from `NiftyNet |
| 753 | <https://github.com/NifTK/NiftyNet>`_. |
| 754 | Referred to `Longitudinal segmentation of age-related white matter hyperintensities |
| 755 | <https://www.sciencedirect.com/science/article/pii/S1361841517300257?via%3Dihub>`_. |
| 756 | |
| 757 | Args: |
| 758 | degree: degree of freedom of the polynomials. The value should be no less than 1. |
| 759 | Defaults to 3. |
| 760 | coeff_range: range of the random coefficients. Defaults to (0.0, 0.1). |
| 761 | dtype: output data type, if None, same as input image. defaults to float32. |
| 762 | prob: probability to do random bias field. |
| 763 | |
| 764 | """ |
| 765 | |
| 766 | backend = [TransformBackends.NUMPY] |
| 767 | |
| 768 | def __init__( |
| 769 | self, |
| 770 | degree: int = 3, |
| 771 | coeff_range: tuple[float, float] = (0.0, 0.1), |
| 772 | dtype: DtypeLike = np.float32, |
| 773 | prob: float = 0.1, |
| 774 | ) -> None: |
| 775 | RandomizableTransform.__init__(self, prob) |
| 776 | if degree < 1: |
| 777 | raise ValueError(f"degree should be no less than 1, got {degree}.") |
| 778 | self.degree = degree |
| 779 | self.coeff_range = coeff_range |
| 780 | self.dtype = dtype |
| 781 | |
| 782 | self._coeff = [1.0] |
| 783 | |
| 784 | def _generate_random_field(self, spatial_shape: Sequence[int], degree: int, coeff: Sequence[float]): |
| 785 | """ |
| 786 | products of polynomials as bias field estimations |
| 787 | """ |
| 788 | rank = len(spatial_shape) |
| 789 | coeff_mat = np.zeros((degree + 1,) * rank) |
| 790 | coords = [np.linspace(-1.0, 1.0, dim, dtype=np.float32) for dim in spatial_shape] |
| 791 | if rank == 2: |
| 792 | coeff_mat[np.tril_indices(degree + 1)] = coeff |
| 793 | return np.polynomial.legendre.leggrid2d(coords[0], coords[1], coeff_mat) |
| 794 | if rank == 3: |
| 795 | pts: list[list[int]] = [[0, 0, 0]] |
| 796 | for i in range(degree + 1): |
| 797 | for j in range(degree + 1 - i): |
| 798 | for k in range(degree + 1 - i - j): |
| 799 | pts.append([i, j, k]) |
| 800 | if len(pts) > 1: |
| 801 | pts = pts[1:] |
| 802 | np_pts = np.stack(pts) |
| 803 | coeff_mat[np_pts[:, 0], np_pts[:, 1], np_pts[:, 2]] = coeff |
no outgoing calls
searching dependent graphs…