Source code for nxtomomill.io.framegroup

# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/

"""
contains the FrameGroup
"""

__authors__ = [
    "H. Payno",
]
__license__ = "MIT"
__date__ = "17/03/2021"


import logging
from typing import Iterable, Union

from silx.io.url import DataUrl
from silx.utils.enum import Enum as _Enum

from .acquisitionstep import AcquisitionStep

_logger = logging.getLogger(__name__)


[docs]class FrameGroup: """class used to store information regarding a group of frame contains the frames type and if we should provide a copy of those in the outputfile or not :param url: url to access the frame group (bliss scan entry) :type url: None, str, DataUrl. If provided as a string should fit a DataUrl or be the data path on the input file :param frame_type: frame type (dark, projection ...) :type param_type: AcquisitionStep, str. If provided as str should be an AcquisitionStep value :param copy: should we copy the dataframe in the output file :type copy: bool, None. If set to None then the value will be set to the default copy behavior during processing """
[docs] class Info(_Enum): FRAME_TYPE = "frame_type" URL_ENTRY = "entry" COPY = "copy"
[docs] def __init__( self, url: Union[DataUrl, str, None], frame_type: [AcquisitionStep, str], copy: Union[bool, None] = None, ): self._url = None self._frame_type = None self._copy = None self._frames_url = None self.url = url self.frame_type = frame_type self.copy = copy
@property def copy(self) -> Union[bool, None]: return self._copy @copy.setter def copy(self, copy: bool): if not isinstance(copy, (bool, type(None))): raise TypeError(f"copy should be a bool and not {type(copy)}") else: self._copy = copy @property def frame_type(self) -> AcquisitionStep: return self._frame_type @frame_type.setter def frame_type(self, frame_type: Union[str, int, AcquisitionStep]): self._frame_type = AcquisitionStep.from_value(frame_type) @property def url(self) -> Union[DataUrl, None]: return self._url @url.setter def url(self, url: Union[DataUrl, str]): if isinstance(url, str): # handle if the string path is not a "full url" then we consider # this is only the data path. self._url = DataUrl(path=url) # small hack. As the url constructor is not able to distinguish # between the data_path or the file_path and set the file path # on our cases we support empty file path ( in this case we # consider this is the input file) but not empty dataset if self._url.data_path() is None and self._url.file_path() is not None: self._url = DataUrl( file_path=None, data_path=self._url.file_path(), scheme=self._url.scheme(), ) elif isinstance(url, (DataUrl, type(None))): self._url = url else: raise TypeError(f"url should be a str or a DataUrl not {type(url)}") def __str__(self) -> str: return self.str_representation( only_data_path=False, with_copy=True, with_prefix_key=True )
[docs] def str_representation( self, only_data_path: bool, with_copy: bool, with_prefix_key: bool ) -> str: """ Util function to print the possible input string for this FrameGroup. :param only_data_path: if True consider the input file frame group is contained in the input file and the string representing the url can be only the data path :type only_data_path: bool :param with_copy: if true display the copy information :type with_copy: bool :param with_prefix_key: if true provide the string with the keys as prefix (frame_type=XXX, copy=...) :type with_prefix_key: bool :rtype: str """ if self.url is None: url_str = "" elif only_data_path: url_str = self.url.data_path() else: url_str = self.url.path() if self.copy is None: with_copy = False if with_prefix_key: if with_copy: return "({ft_key}={frame_type}, {url_key}={url}, {copy_key}={copy})".format( ft_key=self.Info.FRAME_TYPE.value, frame_type=self.frame_type.value, url_key=self.Info.URL_ENTRY.value, url=url_str, copy_key=self.Info.COPY.value, copy=self.copy, ) else: return "({ft_key}={frame_type}, {url_key}={url})".format( ft_key=self.Info.FRAME_TYPE.value, frame_type=self.frame_type.value, url_key=self.Info.URL_ENTRY.value, url=url_str, ) else: if with_copy: return "({frame_type}, {url}, {copy})".format( frame_type=self.frame_type.value, url=url_str, copy=self.copy, ) else: return "({frame_type}, {url})".format( frame_type=self.frame_type.value, url=url_str, )
[docs] @staticmethod def list_to_str(urls: Iterable): """Convert the list of urls to a string that can be pass to the .cfg file""" if len(urls) == 0: return "" else: urls_str = ",\n".join([f"{url}" for url in urls]) return f"""( {urls_str} ) """
[docs] @staticmethod def frm_str(input_str: str): """ Create an instance of FrameGroup from it string representation. """ if not isinstance(input_str, str): raise TypeError(f"{input_str} should be a string") from nxtomomill.io.utils import filter_str_def # avoid cyclic import from nxtomomill.io.utils import ( remove_parenthesis_or_brackets, ) # avoid cyclic import def add_info(info_type, value): if info_type in cst_inputs: err = f"{info_type.value} is provided twice: {value} and {cst_inputs[info_type]}" raise ValueError(err) else: cst_inputs[info_type] = value input_str = remove_parenthesis_or_brackets(input_str) elmts = input_str.split(",") elmts = [elmt.lstrip(" ").rstrip(" ") for elmt in elmts] cst_inputs = {} def treat_elmt(elmt): assert isinstance(elmt, str) # first try to get elements from the "INFO=" format for info in FrameGroup.Info.members(): key = f"{info.value}=" if elmt.startswith(key): add_info( info_type=info, value=filter_str_def(elmt.replace(key, "", 1)) ) return # else try to convert them according to the type # is this an acquisition step elmt = filter_str_def(elmt) try: acquisition_step = AcquisitionStep.from_value(elmt) except ValueError: pass else: add_info(info_type=FrameGroup.Info.FRAME_TYPE, value=acquisition_step) return # is this a copy element if elmt.title() in ("True", "False"): add_info(info_type=FrameGroup.Info.COPY, value=elmt) return try: elmt = filter_str_def(elmt) DataUrl(path=elmt) except Exception: pass else: add_info(info_type=FrameGroup.Info.URL_ENTRY, value=elmt) return url_example = DataUrl( file_path="/path/to/my/file/file.h5", data_path="/data/path", scheme="h5py", ) _example_frame = FrameGroup( url=url_example, copy=True, frame_type="projection" ) err_msg = ( "Unable to interpret string. Please insure this is a " "either a frame type, a boolean for copy or an entry " "(DataUrl).\n " "Please prefix the value by the information type like: " f"{_example_frame}. Invalid element is {input_str}" ) raise ValueError(err_msg) [treat_elmt(elmt) for elmt in elmts] # insure we have at least the frame type and the DataUrl inputs = {} if FrameGroup.Info.FRAME_TYPE not in cst_inputs: raise ValueError(f"Unable to find frame type from {input_str}") else: inputs["frame_type"] = cst_inputs[FrameGroup.Info.FRAME_TYPE] if FrameGroup.Info.URL_ENTRY not in cst_inputs: raise ValueError(f"Unable to find entry from {input_str}") else: inputs["url"] = cst_inputs[FrameGroup.Info.URL_ENTRY] if FrameGroup.Info.COPY in cst_inputs: inputs["copy"] = cst_inputs[FrameGroup.Info.COPY] if "copy" in inputs and inputs["copy"].title() in ("True", "False"): inputs["copy"] = inputs["copy"].title() == "True" return FrameGroup(**inputs)
[docs]def filter_acqui_frame_type( init: FrameGroup, sequences: tuple, frame_type: AcquisitionStep ) -> tuple: """compute the list of urls representing projections from init until the next Initialization step :param FrameGroup init: frame group creating the beginning of the acquisition sequence :param tuple sequences: list of FrameGroup representing the sequence :param AcquisitionStep frame_type: type of frame to filer (cannot be Initialization step) """ frame_type = AcquisitionStep.from_value(frame_type) if frame_type is AcquisitionStep.INITIALIZATION: raise ValueError(f"{AcquisitionStep.INITIALIZATION.value} is not handled") if init not in sequences: raise ValueError(f"{init} cannot be find in the provided sequence") frame_types = [frm_grp.frame_type for frm_grp in sequences] current_acqui_idx = sequences.index(init) if len(sequences) == current_acqui_idx - 1: # in case the initialization sequence is the last element of the # sequence (if people make strange stuff...) return () sequence_target = sequences[current_acqui_idx + 1 :] frame_types = frame_types[current_acqui_idx + 1 :] try: next_acqui = frame_types.index(AcquisitionStep.INITIALIZATION) - 1 except ValueError: next_acqui = -1 sequence_target = sequence_target[:next_acqui] filter_fct = lambda a: a.frame_type is frame_type return tuple(filter(filter_fct, sequence_target))