Ejemplo n.º 1
0
 def test_spacing_torch(self, pixdim, img, track_meta: bool):
     set_track_meta(track_meta)
     tr = Spacing(pixdim=pixdim)
     res = tr(img)
     if track_meta:
         self.assertIsInstance(res, MetaTensor)
         new_spacing = affine_to_spacing(res.affine, 3)
         assert_allclose(new_spacing, pixdim, type_test=False)
         self.assertNotEqual(img.shape, res.shape)
     else:
         self.assertIsInstance(res, torch.Tensor)
         self.assertNotIsInstance(res, MetaTensor)
         self.assertNotEqual(img.shape, res.shape)
     set_track_meta(True)
Ejemplo n.º 2
0
    def get_target_spacing(self,
                           spacing_key: str = "affine",
                           anisotropic_threshold: int = 3,
                           percentile: float = 10.0):
        """
        Calculate the target spacing according to all spacings.
        If the target spacing is very anisotropic,
        decrease the spacing value of the maximum axis according to percentile.
        The spacing is computed from `affine_to_spacing(data[spacing_key][0], 3)` if `data[spacing_key]` is a matrix,
        otherwise, the `data[spacing_key]` must be a vector of pixdim values.

        Args:
            spacing_key: key of the affine used to compute spacing in metadata (default: ``affine``).
            anisotropic_threshold: threshold to decide if the target spacing is anisotropic (default: ``3``).
            percentile: for anisotropic target spacing, use the percentile of all spacings of the anisotropic axis to
                replace that axis.

        """
        if len(self.all_meta_data) == 0:
            self.collect_meta_data()
        if spacing_key not in self.all_meta_data[0]:
            raise ValueError(
                "The provided spacing_key is not in self.all_meta_data.")
        spacings = []
        for data in self.all_meta_data:
            spacing_vals = convert_to_tensor(data[spacing_key][0],
                                             track_meta=False,
                                             wrap_sequence=True)
            if spacing_vals.ndim == 1:  # vector
                spacings.append(spacing_vals[:3][None])
            elif spacing_vals.ndim == 2:  # matrix
                spacings.append(affine_to_spacing(spacing_vals, 3)[None])
            else:
                raise ValueError(
                    "data[spacing_key] must be a vector or a matrix.")
        all_spacings = concatenate(to_cat=spacings, axis=0)
        all_spacings, *_ = convert_data_type(data=all_spacings,
                                             output_type=np.ndarray,
                                             wrap_sequence=True)

        target_spacing = np.median(all_spacings, axis=0)
        if max(target_spacing) / min(target_spacing) >= anisotropic_threshold:
            largest_axis = np.argmax(target_spacing)
            target_spacing[largest_axis] = np.percentile(
                all_spacings[:, largest_axis], percentile)

        output = list(target_spacing)

        return tuple(output)
Ejemplo n.º 3
0
    def create_backend_obj(
        cls,
        data_array: NdarrayOrTensor,
        channel_dim: Optional[int] = 0,
        affine: Optional[NdarrayOrTensor] = None,
        dtype: DtypeLike = np.float32,
        affine_lps_to_ras: bool = True,
        **kwargs,
    ):
        """
        Create an ITK object from ``data_array``. This method assumes a 'channel-last' ``data_array``.

        Args:
            data_array: input data array.
            channel_dim: channel dimension of the data array. This is used to create a Vector Image if it is not ``None``.
            affine: affine matrix of the data array. This is used to compute `spacing`, `direction` and `origin`.
            dtype: output data type.
            affine_lps_to_ras: whether to convert the affine matrix from "LPS" to "RAS". Defaults to ``True``.
                Set to ``True`` to be consistent with ``NibabelWriter``,
                otherwise the affine matrix is assumed already in the ITK convention.
            kwargs: keyword arguments. Current `itk.GetImageFromArray` will read ``ttype`` from this dictionary.

        see also:

            - https://github.com/InsightSoftwareConsortium/ITK/blob/v5.2.1/Wrapping/Generators/Python/itk/support/extras.py#L389
        """
        data_array = super().create_backend_obj(data_array)
        _is_vec = channel_dim is not None
        if _is_vec:
            data_array = np.moveaxis(data_array, -1,
                                     0)  # from channel last to channel first
        data_array = data_array.T.astype(dtype, copy=True, order="C")
        itk_obj = itk.GetImageFromArray(data_array,
                                        is_vector=_is_vec,
                                        ttype=kwargs.pop("ttype", None))

        d = len(itk.size(itk_obj))
        if affine is None:
            affine = np.eye(d + 1, dtype=np.float64)
        _affine = convert_data_type(affine, np.ndarray)[0]
        if affine_lps_to_ras:
            _affine = orientation_ras_lps(to_affine_nd(d, _affine))
        spacing = affine_to_spacing(_affine, r=d)
        _direction: np.ndarray = np.diag(1 / spacing)
        _direction = _affine[:d, :d] @ _direction
        itk_obj.SetSpacing(spacing.tolist())
        itk_obj.SetOrigin(_affine[:d, -1].tolist())
        itk_obj.SetDirection(itk.GetMatrixFromArray(_direction))
        return itk_obj
Ejemplo n.º 4
0
    def test_spacing(self, init_param, img, affine, data_param,
                     expected_output, device):
        img = MetaTensor(img, affine=affine).to(device)
        res: MetaTensor = Spacing(**init_param)(img, **data_param)
        self.assertEqual(img.device, res.device)

        assert_allclose(res, expected_output, atol=1e-1, rtol=1e-1)
        sr = min(len(res.shape) - 1, 3)
        if isinstance(init_param["pixdim"], float):
            init_param["pixdim"] = [init_param["pixdim"]] * sr
        init_pixdim = ensure_tuple(init_param["pixdim"])
        init_pixdim = init_param["pixdim"][:sr]
        norm = affine_to_spacing(res.affine, sr).cpu().numpy()
        assert_allclose(fall_back_tuple(init_pixdim, norm),
                        norm,
                        type_test=False)
Ejemplo n.º 5
0
    def test_orntd_torch(self, init_param, img: torch.Tensor, track_meta: bool,
                         device):
        set_track_meta(track_meta)
        tr = Spacingd(**init_param)
        data = {"seg": img.to(device)}
        res = tr(data)["seg"]

        if track_meta:
            self.assertIsInstance(res, MetaTensor)
            new_spacing = affine_to_spacing(res.affine, 3)
            assert_allclose(new_spacing, init_param["pixdim"], type_test=False)
            self.assertNotEqual(img.shape, res.shape)
        else:
            self.assertIsInstance(res, torch.Tensor)
            self.assertNotIsInstance(res, MetaTensor)
            self.assertNotEqual(img.shape, res.shape)
Ejemplo n.º 6
0
    def test_spacing(self, in_type, init_param, img, data_param,
                     expected_output):
        _img = in_type(img)
        output_data, _, new_affine = Spacing(**init_param)(_img, **data_param)
        if isinstance(_img, torch.Tensor):
            self.assertEqual(_img.device, output_data.device)
            output_data = output_data.cpu()

        np.testing.assert_allclose(output_data,
                                   expected_output,
                                   atol=1e-1,
                                   rtol=1e-1)
        sr = min(len(output_data.shape) - 1, 3)
        if isinstance(init_param["pixdim"], float):
            init_param["pixdim"] = [init_param["pixdim"]] * sr
        init_pixdim = ensure_tuple(init_param["pixdim"])
        init_pixdim = init_param["pixdim"][:sr]
        norm = affine_to_spacing(new_affine, sr)
        np.testing.assert_allclose(fall_back_tuple(init_pixdim, norm), norm)
Ejemplo n.º 7
0
    def get_target_spacing(self,
                           spacing_key: str = "affine",
                           anisotropic_threshold: int = 3,
                           percentile: float = 10.0):
        """
        Calculate the target spacing according to all spacings.
        If the target spacing is very anisotropic,
        decrease the spacing value of the maximum axis according to percentile.
        So far, this function only supports NIFTI images which store spacings in headers with key "pixdim".
        After loading with `monai.DataLoader`, "pixdim" is in the form of `torch.Tensor` with size `(batch_size, 8)`.

        Args:
            spacing_key: key of the affine used to compute spacing in metadata (default: ``affine``).
            anisotropic_threshold: threshold to decide if the target spacing is anisotropic (default: ``3``).
            percentile: for anisotropic target spacing, use the percentile of all spacings of the anisotropic axis to
                replace that axis.

        """
        if len(self.all_meta_data) == 0:
            self.collect_meta_data()
        if spacing_key not in self.all_meta_data[0]:
            raise ValueError(
                "The provided spacing_key is not in self.all_meta_data.")
        spacings = [
            affine_to_spacing(data[spacing_key][0], 3)[None]
            for data in self.all_meta_data
        ]
        all_spacings = concatenate(to_cat=spacings, axis=0)
        all_spacings, *_ = convert_data_type(data=all_spacings,
                                             output_type=np.ndarray,
                                             wrap_sequence=True)

        target_spacing = np.median(all_spacings, axis=0)
        if max(target_spacing) / min(target_spacing) >= anisotropic_threshold:
            largest_axis = np.argmax(target_spacing)
            target_spacing[largest_axis] = np.percentile(
                all_spacings[:, largest_axis], percentile)

        output = list(target_spacing)

        return tuple(output)
Ejemplo n.º 8
0
 def pixdim(self):
     """Get the spacing"""
     if self.is_batch:
         return [affine_to_spacing(a) for a in self.affine]
     return affine_to_spacing(self.affine)
Ejemplo n.º 9
0
 def pixdim(self):
     """Get the spacing"""
     return affine_to_spacing(self.affine)