def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: """ Save data into a Nifti file. The meta_data could optionally have the following keys: - ``'filename_or_obj'`` -- for output file name creation, corresponding to filename or object. - ``'original_affine'`` -- for data orientation handling, defaulting to an identity matrix. - ``'affine'`` -- for data output affine, defaulting to an identity matrix. - ``'spatial_shape'`` -- for data output shape. - ``'patch_index'`` -- if the data is a patch of big image, append the patch index to filename. When meta_data is specified, the saver will try to resample batch data from the space defined by "affine" to the space defined by "original_affine". If meta_data is None, use the default index (starting from 0) as the filename. Args: data: target data content that to be saved as a NIfTI format file. Assuming the data shape starts with a channel dimension and followed by spatial dimensions. meta_data: the meta data information corresponding to the data. See Also :py:meth:`monai.data.nifti_writer.write_nifti` """ filename = meta_data[Key.FILENAME_OR_OBJ] if meta_data else str(self._data_index) self._data_index += 1 original_affine = meta_data.get("original_affine", None) if meta_data else None affine = meta_data.get("affine", None) if meta_data else None spatial_shape = meta_data.get("spatial_shape", None) if meta_data else None patch_index = meta_data.get(Key.PATCH_INDEX, None) if meta_data else None if isinstance(data, torch.Tensor): data = data.detach().cpu().numpy() path = create_file_basename(self.output_postfix, filename, self.output_dir, self.data_root_dir, patch_index) path = f"{path}{self.output_ext}" # change data shape to be (channel, h, w, d) while len(data.shape) < 4: data = np.expand_dims(data, -1) # change data to "channel last" format and write to nifti format file data = np.moveaxis(np.asarray(data), 0, -1) # if desired, remove trailing singleton dimensions if self.squeeze_end_dims: while data.shape[-1] == 1: data = np.squeeze(data, -1) write_nifti( data, file_name=path, affine=affine, target_affine=original_affine, resample=self.resample, output_spatial_shape=spatial_shape, mode=self.mode, padding_mode=self.padding_mode, align_corners=self.align_corners, dtype=self.dtype, output_dtype=self.output_dtype, )
def filename(self, subject: PathLike = "subject", idx=None, **kwargs): """ Create a filename based on the input ``subject`` and ``idx``. The output filename is formed as: ``output_dir/[subject/]subject[_postfix][_idx][_key-value][ext]`` Args: subject: subject name, used as the primary id of the output filename. When a `PathLike` object is provided, the base filename will be used as the subject name, the extension name of `subject` will be ignored, in favor of ``extension`` from this class's constructor. idx: additional index name of the image. kwargs: additional keyword arguments to be used to form the output filename. The key-value pairs will be appended to the output filename as ``f"_{k}-{v}"``. """ full_name = create_file_basename( postfix=self.postfix, input_file_name=subject, folder_path=self.output_dir, data_root_dir=self.data_root_dir, separate_folder=self.parent, patch_index=idx, makedirs=self.makedirs, ) for k, v in kwargs.items(): full_name += f"_{k}-{v}" if self.ext is not None: ext = f"{self.ext}" full_name += f".{ext}" if ext and not ext.startswith(".") else f"{ext}" return full_name
def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: """ Save data into a Nifti file. The meta_data could optionally have the following keys: - ``'filename_or_obj'`` -- for output file name creation, corresponding to filename or object. - ``'original_affine'`` -- for data orientation handling, defaulting to an identity matrix. - ``'affine'`` -- for data output affine, defaulting to an identity matrix. - ``'spatial_shape'`` -- for data output shape. When meta_data is specified, the saver will try to resample batch data from the space defined by "affine" to the space defined by "original_affine". If meta_data is None, use the default index (starting from 0) as the filename. Args: data: target data content that to be saved as a NIfTI format file. Assuming the data shape starts with a channel dimension and followed by spatial dimensions. meta_data: the meta data information corresponding to the data. See Also :py:meth:`monai.data.nifti_writer.write_nifti` """ filename = meta_data["filename_or_obj"] if meta_data else str( self._data_index) for _ in range(self.output_name_uplevel): filename = os.path.dirname(filename) self._data_index += 1 original_affine = meta_data.get("original_affine", None) if meta_data else None affine = meta_data.get("affine", None) if meta_data else None spatial_shape = meta_data.get("spatial_shape", None) if meta_data else None if torch.is_tensor(data): data = data.detach().cpu().numpy() filename = create_file_basename(self.output_postfix, filename, self.output_dir) filename = f"{filename}{self.output_ext}" # change data shape to be (channel, h, w, d) while len(data.shape) < 4: data = np.expand_dims(data, -1) # change data to "channel last" format and write to nifti format file data = np.moveaxis(data, 0, -1) write_nifti( data, file_name=filename, affine=affine, target_affine=original_affine, resample=self.resample, output_spatial_shape=spatial_shape, mode=self.mode, padding_mode=self.padding_mode, align_corners=self.align_corners, dtype=self.dtype, )
def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: """ Save data into a png file. The meta_data could optionally have the following keys: - ``'filename_or_obj'`` -- for output file name creation, corresponding to filename or object. - ``'spatial_shape'`` -- for data output shape. - ``'patch_index'`` -- if the data is a patch of big image, append the patch index to filename. If meta_data is None, use the default index (starting from 0) as the filename. Args: data: target data content that to be saved as a png format file. Assuming the data shape are spatial dimensions. Shape of the spatial dimensions (C,H,W). C should be 1, 3 or 4 meta_data: the meta data information corresponding to the data. Raises: ValueError: When ``data`` channels is not one of [1, 3, 4]. See Also :py:meth:`monai.data.png_writer.write_png` """ filename = meta_data[Key.FILENAME_OR_OBJ] if meta_data else str( self._data_index) self._data_index += 1 spatial_shape = meta_data.get( "spatial_shape", None) if meta_data and self.resample else None patch_index = meta_data.get(Key.PATCH_INDEX, None) if meta_data else None if isinstance(data, torch.Tensor): data = data.detach().cpu().numpy() path = create_file_basename(self.output_postfix, filename, self.output_dir, self.data_root_dir, patch_index) path = f"{path}{self.output_ext}" if data.shape[0] == 1: data = data.squeeze(0) elif 2 < data.shape[0] < 5: data = np.moveaxis(np.asarray(data), 0, -1) else: raise ValueError( f"Unsupported number of channels: {data.shape[0]}, available options are [1, 3, 4]" ) write_png( np.asarray(data), file_name=path, output_spatial_shape=spatial_shape, mode=self.mode, scale=self.scale, )
def test_value(self): with tempfile.TemporaryDirectory() as tempdir: output_tmp = os.path.join(tempdir, "output") result = create_file_basename("", "test.txt", output_tmp, "") expected = os.path.join(output_tmp, "test", "test") self.assertEqual(result, expected) result = create_file_basename("", os.path.join("foo", "test.txt"), output_tmp, "") expected = os.path.join(output_tmp, "test", "test") self.assertEqual(result, expected) result = create_file_basename("", os.path.join("foo", "test.txt"), output_tmp, "foo") expected = os.path.join(output_tmp, "test", "test") self.assertEqual(result, expected) result = create_file_basename("", os.path.join("foo", "bar", "test.txt"), output_tmp, "foo") expected = os.path.join(output_tmp, "bar", "test", "test") self.assertEqual(result, expected) result = create_file_basename( postfix="", input_file_name=os.path.join("foo", "bar", "data", "test.txt"), folder_path=output_tmp, data_root_dir=os.path.join("foo", "bar"), ) expected = os.path.join(output_tmp, "data", "test", "test") self.assertEqual(result, expected) result = create_file_basename("", os.path.join("foo", "bar", "test.txt"), output_tmp, "bar") expected = os.path.join(tempdir, "foo", "bar", "test", "test") self.assertEqual(result, expected) result = create_file_basename("", os.path.join("rest", "test.txt"), output_tmp, "rest") expected = os.path.join(tempdir, "output", "test", "test") self.assertEqual(result, expected) result = create_file_basename("", "test.txt", output_tmp, "foo") expected = os.path.join(output_tmp, "test", "test") self.assertEqual(result, expected) result = create_file_basename("post", "test.tar.gz", output_tmp, "foo") expected = os.path.join(output_tmp, "test", "test_post") self.assertEqual(result, expected)
def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[dict] = None) -> None: """ Save data into a png file. The meta_data could optionally have the following keys: - ``'filename_or_obj'`` -- for output file name creation, corresponding to filename or object. - ``'spatial_shape'`` -- for data output shape. If meta_data is None, use the default index (starting from 0) as the filename. Args: data: target data content that to be saved as a png format file. Assuming the data shape are spatial dimensions. Shape of the spatial dimensions (C,H,W). C should be 1, 3 or 4 meta_data: the meta data information corresponding to the data. Raises: ValueError: PNG image should only have 1, 3 or 4 channels. See Also :py:meth:`monai.data.png_writer.write_png` """ filename = meta_data["filename_or_obj"] if meta_data else str(self._data_index) self._data_index += 1 spatial_shape = meta_data.get("spatial_shape", None) if meta_data and self.resample else None if torch.is_tensor(data): data = data.detach().cpu().numpy() filename = create_file_basename(self.output_postfix, filename, self.output_dir) filename = f"{filename}{self.output_ext}" if data.shape[0] == 1: data = data.squeeze(0) elif 2 < data.shape[0] < 5: data = np.moveaxis(data, 0, -1) else: raise ValueError("PNG image should only have 1, 3 or 4 channels.") write_png( data, file_name=filename, output_spatial_shape=spatial_shape, mode=self.mode, scale=self.scale, )
def test_relative_path(self): output = create_file_basename("", "test.txt", "output", "", makedirs=False) expected = os.path.join("output", "test", "test") self.assertEqual(output, expected)