Beispiel #1
0
    def test_array_gpu(self):
        import cupy as cp

        mv = MedicalVolume(np.ones((10, 20, 30)), self._AFFINE)
        mv_gpu = mv.to(Device(0))
        data = cp.asarray(mv_gpu)
        assert cp.shares_memory(data, mv_gpu.volume)
Beispiel #2
0
    def __calc_quant_vals__(self, quant_map: MedicalVolume,
                            map_type: QuantitativeValueType):
        """Helper method to get quantitative values for tissue - implemented per tissue.

        Different tissues should override this as they see fit.

        Args:
            quant_map (MedicalVolume): 3D map of pixel-wise quantitative measures
                (T2, T2*, T1-rho, etc.). Volume should have ``np.nan`` values for
                all pixels unable to be calculated.
            map_type (QuantitativeValueType): Type of quantitative value to analyze.

        Raises:
            TypeError: If `quant_map` is not of type `MedicalVolume` or `map_type` is not of type
                `QuantitativeValueType`.
            ValueError: If no mask is found for tissue.
        """
        if not isinstance(quant_map, MedicalVolume):
            raise TypeError("`Expected type 'MedicalVolume' for `quant_map`")
        if not isinstance(map_type, QuantitativeValueType):
            raise TypeError(
                "`Expected type 'QuantitativeValueType' for `map_type`")

        if self.__mask__ is None:
            raise ValueError("Please initialize mask for {}".format(
                self.FULL_NAME))

        quant_map.reformat(self.__mask__.orientation, inplace=True)
        pass
Beispiel #3
0
    def test_device_gpu(self):
        import cupy as cp

        mv = MedicalVolume(np.ones((10, 20, 30)), self._AFFINE)
        mv_gpu = mv.to(Device(0))

        assert mv_gpu.device == Device(0)
        assert isinstance(mv_gpu.volume, cp.ndarray)
        assert isinstance(mv_gpu.affine, np.ndarray)

        assert mv_gpu.is_same_dimensions(mv)

        assert cp.all((mv_gpu + 1).volume == 2)
        assert cp.all((mv_gpu - 1).volume == 0)
        assert cp.all((mv_gpu * 2).volume == 2)
        assert cp.all((mv_gpu / 2).volume == 0.5)
        assert cp.all((mv_gpu > 0).volume)
        assert cp.all((mv_gpu >= 0).volume)
        assert cp.all((mv_gpu < 2).volume)
        assert cp.all((mv_gpu <= 2).volume)

        ornt = tuple(x[::-1] for x in mv_gpu.orientation[::-1])
        mv2 = mv_gpu.reformat(ornt)
        assert mv2.orientation == ornt

        mv_cpu = mv_gpu.cpu()
        assert mv_cpu.device == Device(-1)
        assert mv_cpu.is_identical(mv)

        with self.assertRaises(RuntimeError):
            mv_gpu.save_volume(
                os.path.join(self._TEMP_PATH, "test_device.nii.gz"))
Beispiel #4
0
    def _generate_mock_data(self, shape=None, metadata=True):
        """Generates arbitrary mock data for QDess sequence.

        Metadata values were extracted from a real qDESS sequence.
        """
        if shape is None:
            shape = (10, 10, 10)
        e1 = MedicalVolume(np.random.rand(*shape) * 80 + 0.1, affine=np.eye(4))
        e2 = MedicalVolume(np.random.rand(*shape) * 40 + 0.1, affine=np.eye(4))
        ys = [e1, e2]
        ts = [8, 42]
        if metadata:
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                for idx, (y, t) in enumerate(zip(ys, ts)):
                    y.set_metadata("EchoTime", t, force=True)
                    y.set_metadata("EchoNumber", idx + 1, force=True)
                    y.set_metadata("RepetitionTime", 25.0, force=True)
                    y.set_metadata("FlipAngle", 30.0, force=True)
                    y.set_metadata(Tag(0x001910B6), 3132.0,
                                   force=True)  # gradient time
                    y.set_metadata(Tag(0x001910B7), 1560.0,
                                   force=True)  # gradient area

        return ys, ts, None
Beispiel #5
0
def _format_volume_to_header(volume: MedicalVolume) -> MedicalVolume:
    """Reformats the volume according to its header.

    Args:
        volume (MedicalVolume): The volume to reformat.
            Must be 3D and have headers of shape (1, 1, volume.shape[2]).

    Returns:
        MedicalVolume: The reformatted volume.
    """
    headers = volume.headers()
    assert headers.shape == (1, 1, volume.shape[2])

    affine = to_RAS_affine(headers.flatten())
    orientation = stdo.orientation_nib_to_standard(nib.aff2axcodes(affine))

    # Currently do not support mismatch in scanner_origin.
    if tuple(affine[:3, 3]) != volume.scanner_origin:
        raise ValueError(
            "Scanner origin mismatch. "
            "Currently we do not handle mismatch in scanner origin "
            "(i.e. cannot flip across axis)")

    volume = volume.reformat(orientation)
    assert volume.headers().shape == (1, 1, volume.shape[2])
    return volume
Beispiel #6
0
    def test_dtype(self):
        vol = np.ones((10, 20, 30))
        mv = MedicalVolume(vol, self._AFFINE)

        assert mv.volume.dtype == vol.dtype

        mv2 = mv.astype("int32")
        assert id(mv) == id(mv2)
        assert mv2.volume.dtype == np.int32
Beispiel #7
0
    def test_comparison(self):
        mv1 = MedicalVolume(np.ones((10, 20, 30)), self._AFFINE)
        mv2 = MedicalVolume(2 * np.ones((10, 20, 30)), self._AFFINE)

        assert np.all((mv1 == mv1.clone()).volume)
        assert np.all((mv1 != mv2).volume)
        assert np.all((mv1 < mv2).volume)
        assert np.all((mv1 <= mv2).volume)
        assert np.all((mv2 > mv1).volume)
        assert np.all((mv2 >= mv1).volume)
Beispiel #8
0
    def set_mask(self, mask: MedicalVolume):
        """Set mask for tissue.

        Args:
            mask (MedicalVolume): Binary mask of segmented tissue.
        """
        assert type(
            mask
        ) is MedicalVolume, "mask for tissue must be of type MedicalVolume"
        mask.reformat(SAGITTAL, inplace=True)
        self.__mask__ = mask
Beispiel #9
0
    def test_mask(self):
        x, y, b = _generate_monoexp_data((10, 10, 20))
        mask_arr = np.random.rand(*y[0].shape) > 0.5
        mask = MedicalVolume(mask_arr, y[0].affine)

        fitter = CurveFitter(monoexponential)
        popt = fitter.fit(x, y, mask=mask)[0]
        a_hat, b_hat = popt[..., 0], popt[..., 1]

        assert np.allclose(a_hat.volume[mask_arr != 0], 1.0)
        assert np.allclose(b_hat.volume[mask_arr != 0], b[mask_arr != 0])
        assert np.all(np.isnan(a_hat.volume[mask_arr == 0]))
        assert np.all(np.isnan(b_hat.volume[mask_arr == 0]))

        fitter = CurveFitter(monoexponential)
        popt = fitter.fit(x, y, mask=mask_arr)[0]
        a_hat, b_hat = popt[..., 0], popt[..., 1]

        assert np.allclose(a_hat.volume[mask_arr != 0], 1.0)
        assert np.allclose(b_hat.volume[mask_arr != 0], b[mask_arr != 0])

        with self.assertRaises(TypeError):
            fitter = CurveFitter(monoexponential)
            popt = fitter.fit(x, y, mask="foo")[0]

        with self.assertRaises(RuntimeError):
            mask_incorrect_shape = np.random.rand(5, 5, 5) > 0.5
            fitter = CurveFitter(monoexponential)
            popt = fitter.fit(x, y, mask=mask_incorrect_shape)[0]
Beispiel #10
0
    def test_t2_star_map(self):
        ys, _, _, _ = self._generate_mock_data()
        scan = Cones(ys)

        # No mask
        tissue = FemoralCartilage()
        map1 = scan.generate_t2_star_map(tissue,
                                         num_workers=util.num_workers())
        assert map1 is not None, "map should not be None"

        mask = MedicalVolume(np.ones(ys[0].shape), np.eye(4))

        # Use a mask
        tissue.set_mask(mask)
        map2 = scan.generate_t2_star_map(tissue,
                                         num_workers=util.num_workers())
        assert map2 is not None, "map should not be None"
        assert map1.volumetric_map.is_identical(map2.volumetric_map)

        # Use a mask as a path
        tissue = FemoralCartilage()
        mask_path = os.path.join(self.data_dirpath,
                                 "test_t2_star_map_mask.nii.gz")
        NiftiWriter().save(mask, mask_path)
        map2 = scan.generate_t2_star_map(tissue,
                                         num_workers=util.num_workers(),
                                         mask_path=mask_path)
        assert map2 is not None, "map should not be None"
        assert map1.volumetric_map.is_identical(map2.volumetric_map)
Beispiel #11
0
    def load(self, file_path):
        """Load volume from NIfTI file path.

        A NIfTI file should only correspond to one volume.

        Args:
            file_path (str): File path to NIfTI file.

        Returns:
            MedicalVolume: Loaded volume.

        Raises:
            FileNotFoundError: If `file_path` not found.
            ValueError: If `file_path` does not end in a supported NIfTI extension.
        """
        if not os.path.isfile(file_path):
            raise FileNotFoundError("{} not found".format(file_path))

        if not self.data_format_code.is_filetype(file_path):
            raise ValueError(
                "{} must be a file with extension '.nii' or '.nii.gz'".format(
                    file_path))

        nib_img = nib.load(file_path)
        nib_img_affine = nib_img.affine
        nib_img_affine = self.__normalize_affine(nib_img_affine)

        np_img = nib_img.get_fdata()

        return MedicalVolume(np_img, nib_img_affine)
Beispiel #12
0
    def __dilate_mask__(
        self,
        mask_path: str,
        temp_path: str,
        dil_rate: float = preferences.mask_dilation_rate,
        dil_threshold: float = preferences.mask_dilation_threshold,
    ):
        """Dilate mask using gaussian blur and write to disk to use with Elastix.

        Args:
            mask_path (str | MedicalVolume): File path for mask or mask to use to use as
                focus points for registration. Mask must be binary.
            temp_path (str): Directory path to store temporary data.
            dil_rate (`float`, optional): Dilation rate (sigma).
                Defaults to ``preferences.mask_dilation_rate``.
            dil_threshold (`float`, optional): Threshold to binarize dilated mask.
                Must be between [0, 1]. Defaults to ``preferences.mask_dilation_threshold``.

        Returns:
            str: File path of dilated mask.

        Raises:
            FileNotFoundError: If `mask_path` not valid file.
            ValueError: If `dil_threshold` not in range [0, 1].
        """

        if dil_threshold < 0 or dil_threshold > 1:
            raise ValueError("'dil_threshold' must be in range [0, 1]")

        if isinstance(mask_path, MedicalVolume):
            mask = mask_path
        elif os.path.isfile(mask_path):
            mask = fio_utils.generic_load(mask_path, expected_num_volumes=1)
        else:
            raise FileNotFoundError("File {} not found".format(mask_path))

        dilated_mask = (sni.gaussian_filter(np.asarray(mask.volume,
                                                       dtype=np.float32),
                                            sigma=dil_rate) > dil_threshold)
        fixed_mask = np.asarray(dilated_mask, dtype=np.int8)
        fixed_mask_filepath = os.path.join(io_utils.mkdirs(temp_path),
                                           "dilated-mask.nii.gz")

        dilated_mask_volume = MedicalVolume(fixed_mask, affine=mask.affine)
        dilated_mask_volume.save_volume(fixed_mask_filepath)

        return fixed_mask_filepath
Beispiel #13
0
    def test_slice_with_headers(self):
        vol = np.stack([np.ones((10, 20, 30)), 2 * np.ones((10, 20, 30))],
                       axis=-1)
        headers = np.stack(
            [
                ututils.build_dummy_headers(vol.shape[2], {"EchoTime": 2}),
                ututils.build_dummy_headers(vol.shape[2], {"EchoTime": 10}),
            ],
            axis=-1,
        )
        mv = MedicalVolume(vol, self._AFFINE, headers=headers)

        mv2 = mv[..., 0]
        assert mv2._headers.shape == (1, 1, 30)
        for h in mv2.headers(flatten=True):
            assert h["EchoTime"].value == 2

        mv2 = mv[..., 1]
        assert mv2._headers.shape == (1, 1, 30)
        for h in mv2.headers(flatten=True):
            assert h["EchoTime"].value == 10

        mv2 = mv[:10, :5, 8:10, :1]
        assert mv2._headers.shape == (1, 1, 2, 1)

        mv2 = mv[:10]
        assert mv2._headers.shape == (1, 1, 30, 2)
        mv2 = mv[:, :10]
        assert mv2._headers.shape == (1, 1, 30, 2)

        mv2 = mv[..., 0:1]
        assert mv2._headers.shape == (1, 1, 30, 1)

        vol = np.stack([np.ones((10, 20, 30)), 2 * np.ones((10, 20, 30))],
                       axis=-1)
        headers = ututils.build_dummy_headers(vol.shape[2],
                                              {"EchoTime": 2})[..., np.newaxis]
        mv = MedicalVolume(vol, self._AFFINE, headers=headers)
        mv1 = mv[..., 0]
        mv2 = mv[..., 1]
        assert mv1._headers.shape == (1, 1, 30)
        assert mv2._headers.shape == (1, 1, 30)
        for h1, h2 in zip(mv1.headers(flatten=True),
                          mv2.headers(flatten=True)):
            assert id(h1) == id(h2)
Beispiel #14
0
def _generate_translated_vols(n=3):
    """Generate mock data that is translated diagonally by 1 pixel."""
    mvs = []
    affine = to_affine(("SI", "AP"), (0.3, 0.3, 0.5))
    for offset in range(n):
        arr = np.zeros((250, 250, 10))
        arr[15 + offset : 35 + offset, 15 + offset : 35 + offset] = 1
        mvs.append(MedicalVolume(arr, affine))
    return mvs
Beispiel #15
0
    def test_origin(self):
        """Test affine matrix with scanner origin."""
        ornt = ("AP", "SI", "RL")

        origin = np.random.rand(3)
        affine = to_affine(ornt, spacing=None, origin=origin)
        mv = MedicalVolume(np.ones((10, 20, 30)), affine)
        assert mv.orientation == ornt
        assert np.all(np.asarray(mv.pixel_spacing) == 1)
        assert np.all(np.asarray(mv.scanner_origin) == origin)

        origin = np.random.rand(1)
        expected_origin = np.asarray(list(origin) + [0.0, 0.0])
        affine = to_affine(ornt, spacing=None, origin=origin)
        mv = MedicalVolume(np.ones((10, 20, 30)), affine)
        assert mv.orientation == ornt
        assert np.all(np.asarray(mv.pixel_spacing) == 1)
        assert np.all(np.asarray(mv.scanner_origin) == expected_origin)
Beispiel #16
0
    def test_basic(self):
        vol = np.zeros((10, 10, 10))
        mv = MedicalVolume(vol, self._AFFINE)
        with self.assertRaises(TypeError):
            _ = T2(vol)

        qv = T2(mv)
        qv.add_additional_volume("r2", mv + 1)
        assert np.all(qv.additional_volumes["r2"] == mv + 1)
Beispiel #17
0
    def test_spacing(self):
        """Test affine matrix with pixel spacing."""
        ornt = ("AP", "SI", "RL")

        spacing = np.random.rand(3) + 0.1  # avoid pixel spacing of 0
        affine = to_affine(ornt, spacing)
        mv = MedicalVolume(np.ones((10, 20, 30)), affine)
        assert mv.orientation == ornt
        assert np.all(np.asarray(mv.pixel_spacing) == spacing)
        assert np.all(np.asarray(mv.scanner_origin) == 0)

        spacing = np.random.rand(1) + 0.1  # avoid pixel spacing of 0
        expected_spacing = np.asarray(list(spacing) + [1.0, 1.0])
        affine = to_affine(ornt, spacing)
        mv = MedicalVolume(np.ones((10, 20, 30)), affine)
        assert mv.orientation == ornt
        assert np.all(np.asarray(mv.pixel_spacing) == expected_spacing)
        assert np.all(np.asarray(mv.scanner_origin) == 0)
Beispiel #18
0
    def test_to_device(self):
        arr = np.ones((3, 3, 3))
        mv = MedicalVolume(arr, affine=np.eye(4))

        arr2 = to_device(arr, -1)
        assert get_device(arr2) == cpu_device

        mv2 = to_device(mv, -1)
        assert get_device(mv2) == cpu_device
Beispiel #19
0
    def test_from_sitk(self):
        mv = MedicalVolume(np.random.rand(10, 20, 30), self._AFFINE)
        filepath = os.path.join(ututils.TEMP_PATH, "med_vol_from_sitk.nii.gz")
        NiftiWriter().save(mv, filepath)

        nr = NiftiReader()
        expected = nr.load(filepath)

        img = sitk.ReadImage(filepath)
        mv = MedicalVolume.from_sitk(img)

        assert np.allclose(mv.affine, expected.affine)
        assert mv.shape == expected.shape
        assert np.all(mv.volume == expected.volume)

        img = sitk.Image([10, 20, 1], sitk.sitkVectorFloat32, 3)
        mv = MedicalVolume.from_sitk(img)
        assert np.all(mv.volume == 0)
        assert mv.shape == (10, 20, 1, 3)
Beispiel #20
0
 def test_special_affine(self):
     """Test creation of affine matrix for special cases."""
     # Patient orientation (useful for xray data).
     header = ututils.build_dummy_headers(
         1, fields={"PatientOrientation": ["P", "F"], "PixelSpacing": [0.2, 0.5]}
     )
     affine = to_RAS_affine(header)
     mv = MedicalVolume(np.ones((10, 20, 30)), affine=affine)
     assert mv.orientation == ("SI", "AP", "LR")
     assert mv.pixel_spacing == (0.5, 0.2, 1.0)
     assert mv.scanner_origin == (0.0, 0.0, 0.0)
Beispiel #21
0
    def test_slice(self):
        mv = MedicalVolume(np.ones((10, 20, 30)), self._AFFINE)
        with self.assertRaises(IndexError):
            mv[4]
        mv_slice = mv[4:5]
        assert mv_slice.shape == (1, 20, 30)

        mv = MedicalVolume(np.ones((10, 20, 30)), self._AFFINE)
        mv[:5, ...] = 2
        assert np.all(mv._volume[:5, ...] == 2) & np.all(mv._volume[5:,
                                                                    ...] == 1)
        assert np.all(mv[:5, ...].volume == 2)

        mv = MedicalVolume(np.ones((10, 20, 30)), self._AFFINE)
        mv2 = mv[:5, ...].clone()
        mv2 += 2
        mv[:5, ...] = mv2
        assert np.all(mv._volume[:5, ...] == 3) & np.all(mv._volume[5:,
                                                                    ...] == 1)
        assert np.all(mv[:5, ...].volume == 3)
Beispiel #22
0
def _generate_linear_data(shape=None, x=None, a=None):
    """Generate sample linear data.
    ``a`` is randomly generated in interval [0.1, 1.1).
    """
    if a is None:
        a = np.random.rand(*shape) + 0.1
    else:
        shape = a.shape
    if x is None:
        x = np.asarray([0.5, 1.0, 2.0, 4.0])
    y = [MedicalVolume(_linear(t, a), affine=np.eye(4)) for t in x]
    return x, y, a
Beispiel #23
0
    def test_complex(self):
        ornt = ("AP", "SI", "RL")
        spacing = (0.5, 0.7)
        origin = (100, -54)

        expected_spacing = np.asarray(list(spacing) + [1.0])
        expected_origin = np.asarray(list(origin) + [0.0])

        affine = to_affine(ornt, spacing=spacing, origin=origin)
        mv = MedicalVolume(np.ones((10, 20, 30)), affine)
        assert mv.orientation == ornt
        assert np.all(np.asarray(mv.pixel_spacing) == expected_spacing)
        assert np.all(np.asarray(mv.scanner_origin) == expected_origin)
Beispiel #24
0
    def check_orientations(self, mv: MedicalVolume, orientations):
        """
        Apply each orientation specified in orientations to the Medical Volume mv
        Assert if mv --> apply orientation --> apply original orientation != mv original
        position coordinates.

        Args:
            mv: a Medical Volume
            orientations: a list or tuple of orientation tuples
        """
        o_base, so_base, ps_base = mv.orientation, mv.scanner_origin, mv.pixel_spacing
        ps_affine = np.array(mv.affine)

        for o in orientations:
            # Reorient to some orientation
            mv.reformat(o, inplace=True)

            # Reorient to original orientation
            mv.reformat(o_base, inplace=True)

            assert mv.orientation == o_base, "Orientation mismatch: Expected %s, got %s" % (
                str(o_base),
                str(mv.orientation),
            )
            assert mv.scanner_origin == so_base, "Scanner Origin mismatch: Expected %s, got %s" % (
                str(so_base),
                str(mv.scanner_origin),
            )
            assert mv.pixel_spacing == ps_base, "Pixel Spacing mismatch: Expected %s, got %s" % (
                str(ps_base),
                str(mv.pixel_spacing),
            )

            assert (mv.affine == ps_affine).all(
            ), "Affine matrix mismatch: Expected\n%s\ngot\n%s" % (
                str(ps_affine),
                str(mv.affine),
            )
Beispiel #25
0
    def _process_mask(self, mask, y: MedicalVolume):
        """Process mask into appropriate shape."""
        arr_types = (np.ndarray,
                     cp.ndarray) if env.cupy_available() else (np.ndarray, )
        if isinstance(mask, arr_types):
            mask = y._partial_clone(volume=mask, headers=None)
        elif not isinstance(mask, MedicalVolume):
            raise TypeError("`mask` must be a MedicalVolume or ndarray")

        mask = mask.reformat_as(y)
        if not mask.is_same_dimensions(y, defaults.AFFINE_DECIMAL_PRECISION):
            raise RuntimeError("`mask` and `y` dimension mismatch")

        return mask > 0
Beispiel #26
0
def generate_monoexp_data(shape=None, x=None, a=1.0, b=None):
    """Generate sample monoexponetial data.
    ``a=1.0``, ``b`` is randomly generated in interval [0.1, 1.1).

    The equation is :math:`y =  a * \\exp (b*x)`.
    """
    if b is None:
        b = np.random.rand(*shape) + 0.1
    else:
        shape = b.shape
    if x is None:
        x = np.asarray([0.5, 1.0, 2.0, 4.0])
    y = [MedicalVolume(monoexponential(t, a, b), affine=np.eye(4)) for t in x]
    return x, y, a, b
Beispiel #27
0
    def test_basic(self):
        """Basic transposes and flips in RAS+ coordinate system."""
        orientations = [
            ("LR", "PA", "IS"),  # standard RAS+
            ("RL", "AP", "SI"),  # flipped
            ("IS", "LR", "PA"),  # transposed
            ("AP", "SI", "RL"),  # transposed + flipped
        ]

        for ornt in orientations:
            affine = to_affine(ornt)
            mv = MedicalVolume(np.ones((10, 20, 30)), affine)
            assert mv.orientation == ornt
            assert np.all(np.asarray(mv.pixel_spacing) == 1)
            assert np.all(np.asarray(mv.scanner_origin) == 0)
Beispiel #28
0
    def test_save(self):
        filepath = get_testdata_file("MR_small.dcm")
        dr = DicomReader(group_by=None)
        mv_base = dr.load(filepath)[0]

        out_dir = os.path.join(self.data_dirpath, "test_save_sort_by")
        dw = DicomWriter()
        dw.save(mv_base, out_dir, sort_by="InstanceNumber")
        mv2 = dr.load(filepath)[0]
        assert mv2.is_identical(mv_base)

        out_dir = os.path.join(self.data_dirpath, "test_save_no_headers")
        mv = MedicalVolume(np.ones((10, 10, 10)), np.eye(4))
        dw = DicomWriter()
        with self.assertRaises(ValueError):
            dw.save(mv, out_dir)
Beispiel #29
0
def _generate_affine(shape=None, x=None, a=None, b=1.0, as_med_vol=False):
    """Generate data of the form :math:`y = a*x + b`."""
    if a is None:
        a = np.random.rand(*shape) + 0.1
    else:
        shape = a.shape
    if x is None:
        x = np.asarray([0.5, 1.0, 2.0, 4.0])
    if b is None:
        b = np.random.rand(*shape)

    if as_med_vol:
        y = [MedicalVolume(a * t + b, affine=np.eye(4)) for t in x]
    else:
        y = [a * t + b for t in x]
    return x, y, a, b
Beispiel #30
0
    def test_hdf5(self):
        shape = (10, 20, 30)
        volume = np.reshape(list(range(np.product(shape))), shape)
        hdf5_file = os.path.join(self._TEMP_PATH, "unittest.h5")

        with h5py.File(hdf5_file, "w") as f:
            f.create_dataset("volume", data=volume)
        f = h5py.File(hdf5_file, "r")

        mv = MedicalVolume(f["volume"], np.eye(4))
        assert mv.device == Device("cpu")
        assert mv.dtype == f["volume"].dtype

        mv2 = mv[:, :, :1]
        assert np.all(mv2.volume == volume[:, :, :1])
        assert mv2.device == Device("cpu")
        assert mv2.dtype == volume.dtype