Skip to content

Commit

Permalink
1932 core export rits format (#1935)
Browse files Browse the repository at this point in the history
Merged to new RITS branch
  • Loading branch information
samtygier-stfc authored Sep 29, 2023
2 parents c233fce + 6734e15 commit f981096
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/release_notes/next/dev-1932-RITS_export_backend
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#1932 : RITS Export Backend
31 changes: 30 additions & 1 deletion mantidimaging/core/io/saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import List, Union, Optional, Dict, Callable, Tuple, TYPE_CHECKING

import h5py
from pathlib import Path
import numpy as np
from tifffile import tifffile

Expand Down Expand Up @@ -411,7 +412,7 @@ def generate_names(name_prefix: str,
return names


def make_dirs_if_needed(dirname: Optional[str] = None, overwrite_all: bool = False):
def make_dirs_if_needed(dirname: Optional[str] = None, overwrite_all: bool = False) -> None:
"""
Makes sure that the directory needed (for example to save a file)
exists, otherwise creates it.
Expand All @@ -428,3 +429,31 @@ def make_dirs_if_needed(dirname: Optional[str] = None, overwrite_all: bool = Fal
elif os.listdir(path) and not overwrite_all:
raise RuntimeError("The output directory is NOT empty:{0}\nThis can be "
"overridden by specifying 'Overwrite on name conflict'.".format(path))


def create_rits_format(tof: np.ndarray, transmission: np.ndarray, transmission_error: np.ndarray) -> str:
"""
create a RITS format ready for exporting to a .dat file
:param tof: time of flight
:param transmission: transmission value
:param transmission_error: transmission_error value
:return: RITS format ascii
"""
return '\n'.join(
['\t'.join([str(x) for x in row]) for row in zip(tof, transmission, transmission_error, strict=True)])


def export_to_dat_rits_format(rits_formatted_data: str, path: Path) -> None:
"""
export a RITS formatted data to a .dat file
:param rits_formatted_data: RITS formatted data
:param path: path to save the .dat file
:return: None
"""
with open(path, 'w', encoding='utf-8') as f:
f.write(rits_formatted_data)
LOG.info('RITS formatted data saved to: {}'.format(path))
8 changes: 8 additions & 0 deletions mantidimaging/core/io/test/io_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,14 @@ def test_convert_float_to_int(self):
close_arr = np.isclose(conv[i] / factors[i], float_arr[i], rtol=1e-5)
self.assertTrue(np.count_nonzero(close_arr) >= len(close_arr) * 0.75)

def test_create_rits_format(self):
tof = np.array([1, 2, 3])
transmission = np.array([4, 5, 6])
absorption = np.array([7, 8, 9])
rits_formatted_data = saver.create_rits_format(tof, transmission, absorption)
expected = '1\t4\t7\n2\t5\t8\n3\t6\t9'
self.assertEqual(rits_formatted_data, expected)


if __name__ == '__main__':
unittest.main()
28 changes: 28 additions & 0 deletions mantidimaging/gui/windows/spectrum_viewer/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from mantidimaging.core.data import ImageStack
from mantidimaging.core.io.csv_output import CSVOutput
from mantidimaging.core.io import saver
from mantidimaging.core.utility.sensible_roi import SensibleROI

if TYPE_CHECKING:
Expand Down Expand Up @@ -183,6 +184,26 @@ def save_csv(self, path: Path, normalized: bool) -> None:
csv_output.write(outfile)
self.save_roi_coords(self.get_roi_coords_filename(path))

def save_rits(self, path: Path, normalized: bool) -> None:
"""
Saves the spectrum for one ROI to a RITS file.
@param path: The path to save the CSV file to.
@param normalized: Whether to save the normalized spectrum.
"""
if self._stack is None:
raise ValueError("No stack selected")

# Default_roi will likely need updating once UI is implemented
default_roi = self.default_roi_list[0]
tof = np.arange(self._stack.data.shape[0])
transmission_error = np.zeros_like(tof)
if normalized:
if self._normalise_stack is None:
raise RuntimeError("No normalisation stack selected")
transmission = self.get_spectrum(default_roi, SpecType.SAMPLE_NORMED)
self.export_spectrum_to_rits(path, tof, transmission, transmission_error)

def get_roi_coords_filename(self, path: Path) -> Path:
"""
Get the path to save the ROI coordinates to.
Expand All @@ -209,6 +230,13 @@ def save_roi_coords(self, path: Path) -> None:
"Y Max": coords.bottom
})

def export_spectrum_to_rits(self, path: Path, tof, transmission, absorption) -> None:
"""
Export spectrum to RITS format
"""
rits_data = saver.create_rits_format(tof, transmission, absorption)
saver.export_to_dat_rits_format(rits_data, path)

def remove_roi(self, roi_name) -> None:
"""
Remove the selected ROI from the model
Expand Down
15 changes: 15 additions & 0 deletions mantidimaging/gui/windows/spectrum_viewer/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional

from logging import getLogger
from mantidimaging.core.data.dataset import StrictDataset
from mantidimaging.gui.mvp_base import BasePresenter
from mantidimaging.gui.windows.spectrum_viewer.model import SpectrumViewerWindowModel, SpecType
Expand All @@ -13,6 +14,8 @@
from mantidimaging.core.data import ImageStack
from uuid import UUID

LOG = getLogger(__name__)


class SpectrumViewerWindowPresenter(BasePresenter):
"""
Expand Down Expand Up @@ -158,6 +161,18 @@ def handle_export_csv(self) -> None:

self.model.save_csv(path, self.spectrum_mode == SpecType.SAMPLE_NORMED)

def handle_rits_export(self) -> None:
"""
Handle the export of the current spectrum to a RITS file format
"""
path = self.view.get_rits_export_filename()
if path is None:
LOG.debug("No path selected, aborting export")
return
if path.suffix != ".dat":
path = path.with_suffix(".dat")
self.model.save_rits(path, self.spectrum_mode == SpecType.SAMPLE_NORMED)

def handle_enable_normalised(self, enabled: bool) -> None:
if enabled:
self.spectrum_mode = SpecType.SAMPLE_NORMED
Expand Down
12 changes: 12 additions & 0 deletions mantidimaging/gui/windows/spectrum_viewer/test/presenter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ def test_handle_export_csv(self, path_name: str, mock_save_csv: mock.Mock):
self.view.get_csv_filename.assert_called_once()
mock_save_csv.assert_called_once_with(Path("/fake/path.csv"), False)

@parameterized.expand(["/fake/path", "/fake/path.dat"])
@mock.patch("mantidimaging.gui.windows.spectrum_viewer.model.SpectrumViewerWindowModel.save_rits")
def test_handle_rits_export(self, path_name: str, mock_save_rits: mock.Mock):
self.view.get_rits_export_filename = mock.Mock(return_value=Path(path_name))

self.presenter.model.set_stack(generate_images())

self.presenter.handle_rits_export()

self.view.get_rits_export_filename.assert_called_once()
mock_save_rits.assert_called_once_with(Path("/fake/path.dat"), False)

def test_WHEN_do_add_roi_called_THEN_new_roi_added(self):
self.presenter.model.set_stack(generate_images())
self.presenter.do_add_roi()
Expand Down
10 changes: 10 additions & 0 deletions mantidimaging/gui/windows/spectrum_viewer/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ def get_csv_filename(self) -> Optional[Path]:
else:
return None

def get_rits_export_filename(self) -> Optional[Path]:
"""
Get the path to save the RITS file too
"""
path = QFileDialog.getSaveFileName(self, "Save DAT file", "", "DAT file (*.dat)")[0]
if path:
return Path(path)
else:
return None

def set_image(self, image_data: Optional['np.ndarray'], autoLevels: bool = True):
self.spectrum.image.setImage(image_data, autoLevels=autoLevels)

Expand Down

0 comments on commit f981096

Please sign in to comment.