Source code for savu.data.data_structures.preview

# Copyright 2014 Diamond Light Source Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
.. module:: preview
   :platform: Unix
   :synopsis: This class deals with previewing (reduction) of the data and is\
   encapsulated in the Data class.

.. moduleauthor:: Nicola Wadeson <scientificsoftware@diamond.ac.uk>
"""
import numpy as np

from savu.core.utils import docstring_parameter
from savu.plugins.utils import parse_config_string as parse_str
import savu.data.data_structures.data_notes as notes


[docs]class Preview(object): """The Data class dynamically inherits from transport specific data class and holds the data array, along with associated information. """ def __init__(self, data_obj): self._data_obj = data_obj self.revert_shape = None self.revert_axis_labels = None self.plugin_preview = None
[docs] def get_data_obj(self): return self._data_obj
[docs] @docstring_parameter(notes._set_preview_note.__doc__) def set_preview(self, preview_list, **kwargs): """ Reduces the data to be processed to a subset of the original. :param list preview: previewing parameters, where ``len(preview_list)`` equals the number of data dimensions. :keyword bool revert: revert input dataset to the original size after plugin processing. {0} """ self.revert_shape = kwargs.get('revert', self.revert_shape) load = kwargs.get('load', False) shape = self.get_data_obj().get_shape() # for backward compatibility preview_list = parse_str(preview_list) if \ isinstance(preview_list, str) else preview_list preview_list = self.__convert_nprocs(preview_list) if preview_list: preview_list = self._add_preview_defaults(preview_list) starts, stops, steps, chunks = \ self._get_preview_indices(preview_list) shape_change = True else: starts, stops, steps, chunks = \ [[0]*len(shape), shape, [1]*len(shape), [1]*len(shape)] shape_change = False self.__set_starts_stops_steps(starts, stops, steps, chunks, shapeChange=shape_change, load=load) self.__check_preview_indices()
def __convert_nprocs(self, preview_list): for i in range(len(preview_list)): if preview_list[i] == 'nprocs': nprocs = self.get_data_obj().exp.meta_data.get('nProcesses') start = int(np.floor(nprocs / 2.0)) end = int(np.ceil(nprocs / 2.0)) preview_list[i] = f'mid-{start}:mid-{end}' return preview_list def _add_preview_defaults(self, plist): """ Fill in missing values in preview list entries. :param: preview list with entries of the form ``start[:stop:step:chunk]`` :returns: preview list with missing values replaced by defaults :rtype: list """ nEntries = 4 #plist = [str(i) for i in plist] if isinstance(plist[0], int) else plist plist = [str(i) if isinstance(i, int) else i for i in plist] diff_len = [(nEntries - len(elem.split(':'))) for elem in plist] diff3 = [i for i in range(len(diff_len)) if diff_len[i] == 3] for dim in diff3: plist[dim] = plist[dim] + ':' + plist[dim] + '+1' diff_len[dim] = 2 all_idx = [i for i in range(len(plist)) if plist[i] == ':'] amend = [i for i in range(len(plist)) if diff_len and i not in all_idx] for idx in amend: plist[idx] += ':1'*diff_len[idx] return plist def _unset_preview(self): """ Unset preview (revert=True) if it was only required in the plugin. """ self._data_obj.set_shape(self.revert_shape) self.set_preview([]) self.revert_shape = None def _reset_preview(self): self.set_preview([]) self.plugin_preview = None def __set_starts_stops_steps(self, starts, stops, steps, chunks, shapeChange=True, load=False): """ Add previewing params to data_info dictionary and set reduced shape. """ set_mData = self.get_data_obj().data_info.set set_mData('starts', starts) set_mData('stops', stops) set_mData('steps', steps) set_mData('chunks', chunks) if shapeChange: self.__set_reduced_shape(starts, stops, steps, chunks) slice_list = self._get_preview_slice_list() self.get_data_obj().amend_axis_label_values(slice_list) if load: self.__add_preview_param('starts', starts) self.__add_preview_param('stops', stops) self.__add_preview_param('steps', steps) def __add_preview_param(self, name, value): entry = self.get_data_obj().get_name() + '_preview_' + name self.get_data_obj().exp.meta_data.set(entry, value) def _get_preview_indices(self, preview_list): """ Get preview_list ``starts``, ``stops``, ``steps``, ``chunks`` separate components with integer values. :params: preview_list :returns: separate list of starts, stops, steps, chunks integer values :rtype: list(list(int)) """ starts = len(preview_list)*[None] stops = len(preview_list)*[None] steps = len(preview_list)*[None] chunks = len(preview_list)*[None] for i in range(len(preview_list)): if preview_list[i] == ':': preview_list[i] = '0:end:1:1' vals = preview_list[i].split(':') starts[i], stops[i], steps[i], chunks[i] = \ self.convert_indices(vals, i) return starts, stops, steps, chunks
[docs] def get_integer_entries(self, plist): """ Convert Savu preview syntax to python slicing (similar) syntax, by replacing Savu Built-in constants. Parameters ---------- plist : list A Savu data preview list to reduce the data dimensions. Returns ------- list A Savu preview list containing integers and no strings. """ if plist: vals = self._get_preview_indices(self._add_preview_defaults(plist)) else: shape = self.get_data_obj().get_shape() vals = [[0]*len(shape), shape, [1]*len(shape), [1]*len(shape)] return [':'.join(map(str, l)) for l in list(zip(*vals))]
[docs] def convert_indices(self, idx, dim): """ convert keywords to integers. """ dobj = self.get_data_obj() shape = dobj.get_shape() start = 0 mid = np.clip(np.ceil(shape[dim] / 2.0).astype('int') - 1, 0, None) end = shape[dim] idx = [eval(equ, {"builtins": None}, {"start": start, "mid": mid, "end": end}) for equ in idx] idx = [idx[i] if idx[i] > -1 else shape[dim]+1+idx[i] for i in range(len(idx))] return idx
[docs] def get_starts_stops_steps(self, key=None): """ Returns preview parameter ``starts``, ``stops``, ``steps``, ``chunks`` lists. :keyword str key: the list to return. :returns: if key is none return separate preview_list components, where each list has length equal to number of dataset dimensions, else only the ``key`` list. :rtype: list(list(int)) """ mData = self.get_data_obj().data_info if 'starts' not in list(mData.get_dictionary().keys()): return None if key else [None]*4 if key is not None: return mData.get(key) return [mData.get('starts'), mData.get('stops'), mData.get('steps'), mData.get('chunks')]
def __check_preview_indices(self): starts, stops, steps, chunks = self.get_starts_stops_steps() nDims = len(starts) for i in range(nDims): if stops[i] <= starts[i]: raise Exception("Error in previewing parameters! " "Check parameters that may alter data dimensions. " "(axis={}, start={}, stop={})".format( i, starts[i], stops[i])) def __set_reduced_shape(self, starts, stops, steps, chunks): """ Set new shape if data is reduced by previewing. """ dobj = self.get_data_obj() td = dobj._get_transport_data() orig_shape = dobj.get_shape() dobj.data_info.set('orig_shape', orig_shape) new_shape = [] for dim in range(len(starts)): new_shape.append(np.prod((td._get_slice_dir_matrix(dim).shape))) dobj.set_shape(tuple(new_shape)) def _get_preview_slice_list(self): """ Amend the axis label values based on the previewing parameters. """ dobj = self.get_data_obj() td = dobj._get_transport_data() starts, stops, steps, chunks = self.get_starts_stops_steps() if starts is None: return None slice_list = [] for dim in range(len(dobj.get_shape())): if chunks[dim] > 1: slice_list.append( np.ravel(np.transpose(td._get_slice_dir_matrix(dim)))) else: slice_list.append(slice(starts[dim], stops[dim], steps[dim])) return slice_list