# Copyright (C) 2022 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
import os
from dataclasses import dataclass
from logging import getLogger, Logger
from pathlib import Path
from typing import Tuple, List, Optional, Union, TYPE_CHECKING
import numpy as np
from mantidimaging.core.data.dataset import StrictDataset
if TYPE_CHECKING:
import numpy.typing as npt
from mantidimaging.core.data import Images
from mantidimaging.core.io.loader import img_loader
from mantidimaging.core.io.utility import (DEFAULT_IO_FILE_FORMAT, get_file_names, get_prefix, get_file_extension,
find_images, find_first_file_that_is_possibly_a_sample, find_log,
find_180deg_proj)
from mantidimaging.core.utility.data_containers import ImageParameters, LoadingParameters, Indices
from mantidimaging.core.utility.imat_log_file_parser import IMATLogFile
from mantidimaging.core.utility.progress_reporting import Progress
LOG = getLogger(__name__)
DEFAULT_IS_SINOGRAM = False
DEFAULT_PIXEL_SIZE = 0
DEFAULT_PIXEL_DEPTH = "float32"
def _fitsread(filename: str) -> np.ndarray:
"""
Read one image and return it as a 2d numpy array
:param filename :: name of the image file, can be relative or absolute path
:param img_format: format of the image ('fits')
"""
import astropy.io.fits as fits
image = fits.open(filename)
if len(image) < 1:
raise RuntimeError("Could not load at least one FITS image/table file from: {0}".format(filename))
# get the image data
return image[0].data
def _nxsread(filename: str) -> np.ndarray:
import h5py
nexus = h5py.File(filename, 'r')
data = nexus["tomography/sample_data"]
return data
def _imread(filename: str) -> np.ndarray:
from mantidimaging.core.utility.special_imports import import_skimage_io
skio = import_skimage_io()
return skio.imread(filename)
[docs]
def load_log(log_file: str) -> IMATLogFile:
with open(log_file, 'r') as f:
return IMATLogFile(f.readlines(), log_file)
[docs]
def load_p(parameters: ImageParameters, dtype: 'npt.DTypeLike', progress: Progress) -> Images:
return load(input_path=parameters.input_path,
in_prefix=parameters.prefix,
in_format=parameters.format,
indices=parameters.indices,
dtype=dtype,
progress=progress).sample
[docs]
def load_stack(file_path: str, progress: Optional[Progress] = None) -> Images:
image_format = get_file_extension(file_path)
prefix = get_prefix(file_path)
file_names = get_file_names(path=os.path.dirname(file_path), img_format=image_format, prefix=prefix) # type: ignore
return load(file_names=file_names, progress=progress).sample
[docs]
def load(input_path: Optional[str] = None,
input_path_flat_before: Optional[str] = None,
input_path_flat_after: Optional[str] = None,
input_path_dark_before: Optional[str] = None,
input_path_dark_after: Optional[str] = None,
in_prefix: str = '',
in_format: str = DEFAULT_IO_FILE_FORMAT,
dtype: 'npt.DTypeLike' = np.float32,
file_names: Optional[List[str]] = None,
indices: Optional[Union[List[int], Indices]] = None,
progress: Optional[Progress] = None) -> StrictDataset:
"""
Loads a stack, including sample, white and dark images.
:param input_path: Path for the input data folder
:param input_path_flat_before: Optional: Path for the input Flat Before images folder
:param input_path_flat_after: Optional: Path for the input Flat After images folder
:param input_path_dark_before: Optional: Path for the input Dark Before images folder
:param input_path_dark_after: Optional: Path for the input Dark After images folder
:param in_prefix: Optional: Prefix for loaded files
:param in_format: Default:'tiff', format for the input images
:param dtype: Default:np.float32, data type for the input images
:param file_names: Use provided file names for loading
:param indices: Specify which indices are loaded from the found files.
This **DOES NOT** check for the number in the image
filename, but removes all indices from the filenames list
that are not selected
:param progress: The progress reporting instance
:return: a tuple with shape 3: (sample, flat, dark), if no flat and dark
were loaded, they will be None
"""
if in_format not in supported_formats():
raise ValueError("Image format {0} not supported!".format(in_format))
if indices and len(indices) < 3:
raise ValueError("Indices at this point MUST have 3 elements: [start, stop, step]!")
if not file_names:
input_file_names = get_file_names(input_path, in_format, in_prefix)
else:
input_file_names = file_names
if in_format in ['nxs']:
raise NotImplementedError("TODO this needs to be adapted to the new changes")
# pass only the first filename as we only expect a stack
# input_file = input_file_names[0]
# images = stack_loader.execute(_nxsread, input_file, dtype, "NXS Load", indices, progress)
else:
if in_format in ['fits', 'fit']:
load_func = _fitsread
else:
load_func = _imread
dataset = img_loader.execute(load_func, input_file_names, input_path_flat_before, input_path_flat_after,
input_path_dark_before, input_path_dark_after, in_format, dtype, indices, progress)
# Search for and load metadata file
metadata_found_filenames = get_file_names(input_path, 'json', in_prefix, essential=False)
metadata_filename = metadata_found_filenames[0] if metadata_found_filenames else None
if metadata_filename:
with open(metadata_filename) as f:
dataset.sample.load_metadata(f)
LOG.debug('Loaded metadata from: {}'.format(metadata_filename))
else:
LOG.debug('No metadata file found')
return dataset
[docs]
def find_and_verify_sample_log(sample_directory: str, image_filenames: List[str]) -> str:
sample_log = find_log(dirname=Path(sample_directory), log_name=sample_directory)
log = load_log(sample_log)
log.raise_if_angle_missing(image_filenames)
return sample_log
[docs]
def create_loading_parameters_for_file_path(file_path: str,
logger: Optional[Logger] = None) -> Optional[LoadingParameters]:
sample_file = find_first_file_that_is_possibly_a_sample(file_path)
if sample_file is None:
return None
loading_parameters = LoadingParameters()
loading_parameters.dtype = DEFAULT_PIXEL_DEPTH
loading_parameters.pixel_size = DEFAULT_PIXEL_SIZE
loading_parameters.sinograms = DEFAULT_IS_SINOGRAM
loading_parameters.name = os.path.basename(sample_file)
_, image_format = os.path.splitext(sample_file)
sample_directory = os.path.dirname(sample_file)
last_file_info = read_in_file_information(sample_directory,
in_prefix=get_prefix(sample_file),
in_format=image_format)
try:
sample_log: Optional[str] = find_and_verify_sample_log(sample_directory, last_file_info.filenames)
except FileNotFoundError:
sample_log = None
loading_parameters.sample = ImageParameters(input_path=sample_directory,
format=image_format,
prefix=get_prefix(sample_file),
log_file=sample_log)
# Flat before
flat_before_images = find_images(Path(sample_directory),
"Flat",
suffix="Before",
look_without_suffix=True,
image_format=image_format,
logger=logger)
if len(flat_before_images) > 0:
flat_before_image = flat_before_images[0]
flat_before_directory = os.path.dirname(flat_before_image)
flat_before_log = find_log(Path(sample_directory), flat_before_directory, logger)
loading_parameters.flat_before = ImageParameters(input_path=flat_before_directory,
format=image_format,
prefix=get_prefix(flat_before_image),
log_file=flat_before_log)
# Flat after
flat_after_images = find_images(Path(sample_directory),
"Flat",
suffix="After",
image_format=image_format,
logger=logger)
if len(flat_after_images) > 0:
flat_after_image = flat_after_images[0]
flat_after_directory = os.path.dirname(flat_after_image)
flat_after_log = find_log(Path(sample_directory), flat_after_directory, logger)
loading_parameters.flat_after = ImageParameters(input_path=flat_after_directory,
format=image_format,
prefix=get_prefix(flat_after_image),
log_file=flat_after_log)
# Dark before
dark_before_images = find_images(Path(sample_directory),
"Dark",
suffix="Before",
look_without_suffix=True,
image_format=image_format,
logger=logger)
if len(dark_before_images) > 0:
dark_before_image = dark_before_images[0]
dark_before_directory = os.path.dirname(dark_before_image)
loading_parameters.dark_before = ImageParameters(input_path=dark_before_directory,
prefix=get_prefix(dark_before_image),
format=image_format)
# Dark after
dark_after_images = find_images(Path(sample_directory),
"Dark",
suffix="After",
image_format=image_format,
logger=logger)
if len(dark_after_images) > 0:
dark_after_image = dark_after_images[0]
dark_after_directory = os.path.dirname(dark_after_image)
loading_parameters.dark_after = ImageParameters(input_path=dark_after_directory,
prefix=get_prefix(dark_after_image),
format=image_format)
# 180 Degree projection
proj_180deg = find_180deg_proj(Path(sample_directory), image_format, logger)
if proj_180deg != "":
loading_parameters.proj_180deg = ImageParameters(input_path=proj_180deg,
prefix=get_prefix(proj_180deg),
format=image_format)
return loading_parameters