Source code for seek_localize.utils.io
import bz2
import contextlib
import json
import pickle
from pathlib import Path
from typing import Dict, Union
import numpy as np
import scipy.io
[docs]def read_fieldtrip_elecs(elec_fname: Union[str, Path], verbose: bool = True) -> Dict:
"""Read fieldtrip localization output matlab structure.
The FieldTrip output will contain the channel names,
and an array of the 3D coordinates in mm space.
Parameters
----------
elec_fname : str | pathlib.Path
The file path to the ``.mat`` file.
verbose : bool
Verbosity.
Returns
-------
eleccoords_mm : dict
The electrode coordinates in mm with channel name (key) and
the 3D coordinates (value).
"""
if verbose:
print(
f"Reading fieldtrip localization output matlab structure "
f"from {elec_fname}."
)
eleccoords_mm = {}
matreader = MatReader()
data = matreader.loadmat(elec_fname).get("elec_acpc_f")
if verbose:
print(f"Read in data with keys: {data.keys()}")
# eleclabels = data["eleclabels"]
# elecmatrix = data["elecmatrix"]
eleclabels = data["label"]
elecmatrix = data["chanpos"]
# print(f"Electrode matrix shape: {elecmatrix.shape}")
for i in range(len(eleclabels)):
eleccoords_mm[eleclabels[i][0].strip()] = elecmatrix[i]
return eleccoords_mm
class MatReader:
"""
Object to read mat files into a nested dictionary if need be.
Helps keep structure from matlab similar to what is used in python.
"""
def __init__(self, filename=None):
self.filename = filename
def loadmat(self, filename):
"""
this function should be called instead of direct spio.loadmat
as it cures the problem of not properly recovering python dictionaries
from mat files. It calls the function check keys to cure all entries
which are still mat-objects
"""
data = scipy.io.loadmat(filename, struct_as_record=False, squeeze_me=True)
return self._check_keys(data)
def _check_keys(self, dict):
"""
checks if entries in dictionary are mat-objects. If yes
todict is called to change them to nested dictionaries
"""
for key in dict:
if isinstance(dict[key], scipy.io.matlab.mio5_params.mat_struct):
dict[key] = self._todict(dict[key])
return dict
def _todict(self, matobj):
"""
A recursive function which constructs from matobjects nested dictionaries
"""
dict = {}
for strg in matobj._fieldnames:
elem = matobj.__dict__[strg]
if isinstance(elem, scipy.io.matlab.mio5_params.mat_struct):
dict[strg] = self._todict(elem)
elif isinstance(elem, np.ndarray):
dict[strg] = self._tolist(elem)
else:
dict[strg] = elem
return dict
def _tolist(self, ndarray):
"""
A recursive function which constructs lists from cellarrays
(which are loaded as numpy ndarrays), recursing into the elements
if they contain matobjects.
"""
elem_list = []
for sub_elem in ndarray:
if isinstance(sub_elem, scipy.io.matlab.mio5_params.mat_struct):
elem_list.append(self._todict(sub_elem))
elif isinstance(sub_elem, np.ndarray):
elem_list.append(self._tolist(sub_elem))
else:
elem_list.append(sub_elem)
return elem_list
def convertMatToJSON(self, matData, fileName):
jsonData = {}
for key in matData.keys():
if (type(matData[key])) is np.ndarray:
serializedData = pickle.dumps(
matData[key], protocol=0
) # protocol 0 is printable ASCII
jsonData[key] = serializedData
else:
jsonData[key] = matData[key]
with contextlib.closing(bz2.BZ2File(fileName, "wb")) as f:
json.dump(jsonData, f)