Source code for arim.io.brain

"""
I/O module for BRAIN files (Matlab NDT library of Universiy of Bristol).


Implemented as of 20/6/2016:
- dtype of variables is according to settings.py

- get element dimensions from el_x1, el_y1, el_z1, el_x2, el_y2, el_z2:
  Information calculated is probe orientation dependent.


"""

import numpy as np

from .. import geometry as g
from .. import settings as s
from ..core import ExaminationObject, Frame, Material, Probe, Time

__all__ = ["load_expdata"]


class NotHandledByScipy(Exception):
    pass


class InvalidExpData(IOError):
    pass


def _import_h5py():
    try:
        import h5py
    except ImportError:
        h5py = None
    return h5py


[docs] def load_expdata(file): """ Load exp_data file. Parameters ---------- file: str or file object Returns ------- arim.core.Frame Raises ------ InvalidExpData, OSError (HDF5 fail) """ try: (exp_data, array, filename) = _load_from_scipy(file) except NotHandledByScipy: # It seems the file is HDF5 (matlab 7.3) h5py = _import_h5py() if h5py is None: raise Exception( "Unable to import Matlab file because its file format version is unsupported. " "Try importing the file in Matlab and exporting it with the " "command 'save' and the flag '-v7'. Alternatively, try to install the Python library 'h5py'." ) (exp_data, array, filename) = _load_from_hdf5(file) # As this point exp_data and array are populated either by scipy.io or hdf5: try: probe = _load_probe(array) except Exception as e: raise InvalidExpData(e) from e try: frame = _load_frame(exp_data, probe) except Exception as e: raise InvalidExpData(e) from e frame.metadata["from_brain"] = filename frame.probe.metadata["from_brain"] = filename frame.examination_object.metadata["from_brain"] = filename return frame
def _load_probe(array): """ :param array: dict-like object corresponding to Matlab struct exp_data.array. :return: Probe """ frequency = array["centre_freq"][0, 0] # dtype = np.result_type(array['el_xc'], array['el_yc'], array['el_zc']) dtype = s.FLOAT # Get locations locations_x = np.squeeze(array["el_xc"]).astype(dtype) locations_y = np.squeeze(array["el_yc"]).astype(dtype) locations_z = np.squeeze(array["el_zc"]).astype(dtype) locations = g.Points.from_xyz(locations_x, locations_y, locations_z) # Calculate Probe Dimensions (using el_x1, el_x2 and el_xc etc for each dimension) dimensions_x = 2 * np.maximum( np.absolute(np.squeeze(array["el_x1"]).astype(dtype) - locations_x), np.absolute(np.squeeze(array["el_x2"]).astype(dtype) - locations_x), ) dimensions_y = 2 * np.maximum( np.absolute(np.squeeze(array["el_y1"]).astype(dtype) - locations_y), np.absolute(np.squeeze(array["el_y2"]).astype(dtype) - locations_y), ) dimensions_z = 2 * np.maximum( np.absolute(np.squeeze(array["el_z1"]).astype(dtype) - locations_z), np.absolute(np.squeeze(array["el_z2"]).astype(dtype) - locations_z), ) dimensions = g.Points.from_xyz(dimensions_x, dimensions_y, dimensions_z) return Probe(locations, frequency, dimensions=dimensions) def _load_frame(exp_data, probe): # NB: Matlab is 1-indexed, Python is 0-indexed tx = np.squeeze(exp_data["tx"]) rx = np.squeeze(exp_data["rx"]) tx = tx.astype(s.UINT) - 1 rx = rx.astype(s.UINT) - 1 # Remark: [...] is required to read in the case of HDF5 file # (and does nothing if we have a regular array timetraces = np.squeeze(exp_data["time_data"][...]) timetraces = timetraces.astype(s.FLOAT) # exp_data.time_data is such as a two consecutive time samples are stored contiguously, which # is what we want. However Matlab saves either in Fortran order (shape: numtimetraces x numsamples) # or C order (shape: numsamples x numtimetraces). We force using the later case. if timetraces.flags.f_contiguous: timetraces = timetraces.T timevect = np.squeeze(exp_data["time"]) timevect = timevect.astype(s.FLOAT) time = Time.from_vect(timevect) try: velocity = np.squeeze(exp_data["material"]["vel_spherical_harmonic_coeffs"]) if isinstance(velocity[()], np.ndarray): # Accept nested array velocity = velocity[()] # Old version of brain saves phase velocity, new version has it saved in material. except (ValueError, KeyError): velocity = np.squeeze(exp_data["ph_velocity"]) # Sometimes have location saved in `exp_data`. Useful to have access to this info. metadata = None try: location = { k: float(exp_data["location"][k][0, 0]) for k in exp_data["location"].dtype.names } metadata = {"location": location} except AttributeError: location = {k: float(v[0, 0]) for k, v in exp_data["location"].items()} metadata = {"location": location} except (ValueError, KeyError): pass velocity = velocity.astype(s.FLOAT) material = Material(velocity) examination_object = ExaminationObject(material) return Frame(timetraces, time, tx, rx, probe, examination_object, metadata) def _load_from_scipy(file): """ :param file: :return: :raises: NotHandledByScipy """ import scipy.io as sio try: data = sio.loadmat(file) except NotImplementedError as e: raise NotHandledByScipy(e) # Get data: try: exp_data = data["exp_data"][0, 0] array = exp_data["array"][0, 0] except IndexError as e: raise InvalidExpData(e) from e # Get filename (works whether 'file' is a file object or a (str) filename) try: filename = file.name except AttributeError: filename = str(file) return exp_data, array, filename def _load_from_hdf5(file): import h5py # This line might raise an OSError: f = h5py.File(file, mode="r") try: # File successfully loaded by HDF5: exp_data = f["exp_data"] array = exp_data["array"] except IndexError as e: raise InvalidExpData(e) from e filename = f.filename return exp_data, array, filename