Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fif2edf #114

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ omit =
*/setup.py
*/tests/*
*/notebooks/*
*/tinnsleep/config.py
*/tinnsleep/config.py
*/tinnsleep/external/*
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#Notebook draft
drafts/
Notebooks/*_vbrouillon.*
tests/dummy_fif.edf

# Byte-compiled / optimized / DLL files
results/
Expand Down
Binary file added tests/dummy_fif.edf
Binary file not shown.
Binary file added tests/dummy_fif.fif
Binary file not shown.
2 changes: 0 additions & 2 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,5 +282,3 @@ def test_read_etiology_file_real():
data_info_file = os.path.join(os.path.dirname(__file__), "../notebooks/data/data_info.csv")
data_info = pd.read_csv(data_info_file, sep=";")
data_info.merge(df_etiology, on="subject")


35 changes: 35 additions & 0 deletions tests/test_io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import mne
import os
import numpy as np
import numpy.testing as npt
from tinnsleep.external.save_edf import write_edf
import matplotlib.pyplot as plt

def test_fif2edf():
fname = "./dummy_fif"
fname_edf = fname +".edf"
raw = mne.io.read_raw_fif(fname+".fif", preload=True)

# plt.close("all")
# plt.figure()
# raw.plot()
# plt.show()

signals = raw.get_data()
N, T = signals.shape
scaling = np.ones((signals.shape[0]))
scaling[np.median(np.abs(signals), axis=1)< 1e-3] = 1e6
#raw.info["ch_names"]
if os.path.exists(fname_edf):
os.remove(fname_edf)
write_edf(raw, fname_edf)

raw2 = mne.io.read_raw_edf(fname_edf)
signals2 = raw2.get_data()
is_uV = np.median(np.abs(signals), axis=1)< 1e-3
npt.assert_allclose(signals2[is_uV,:T], signals[is_uV, :], atol=0.01)
npt.assert_allclose(signals2[~is_uV,:T], signals[~is_uV, :], atol=0.01)

# plt.figure()
# raw2.plot()
# plt.show()
2 changes: 2 additions & 0 deletions tinnsleep/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,5 @@ def read_etiology_file(etiology_file,
df_etiology[new_key] = df_raw[key].replace(mapping) == 1

return df_etiology


2 changes: 2 additions & 0 deletions tinnsleep/external/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- mne
- pyedflib
122 changes: 122 additions & 0 deletions tinnsleep/external/save_edf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 5 12:56:31 2018

@author: skjerns

Gist to save a mne.io.Raw object to an EDF file using pyEDFlib
(https://github.com/holgern/pyedflib)

Disclaimer:
- Saving your data this way will result in slight
loss of precision (magnitude +-1e-09).
- It is assumed that the data is presented in Volt (V), it will be internally converted to microvolt
- Saving to BDF can be done by changing the file_type variable.
Be aware that you also need to change the dmin and dmax to
the corresponding minimum and maximum integer values of the
file_type: e.g. BDF+ dmin, dmax =- [-8388608, 8388607]
"""

import pyedflib # pip install pyedflib
from datetime import datetime
import mne
import os
import numpy as np


def write_edf(mne_raw, fname, picks=None, tmin=0, tmax=None, overwrite=False):
"""
Saves the raw content of an MNE.io.Raw and its subclasses to
a file using the EDF+ filetype
pyEDFlib is used to save the raw contents of the RawArray to disk

Parameters
----------
mne_raw : mne.io.Raw
An object with super class mne.io.Raw that contains the data
to save
fname : string
File name of the new dataset. This has to be a new filename
unless data have been preloaded. Filenames should end with .edf
picks : array-like of int | None
Indices of channels to include. If None all channels are kept.
tmin : float | None
Time in seconds of first sample to save. If None first sample
is used.
tmax : float | None
Time in seconds of last sample to save. If None last sample
is used.
overwrite : bool
If True, the destination file (if it exists) will be overwritten.
If False (default), an error will be raised if the file exists.
"""
if not issubclass(type(mne_raw), mne.io.BaseRaw):
raise TypeError('Must be mne.io.Raw type')
if not overwrite and os.path.exists(fname):
raise OSError('File already exists. No overwrite.')
# static settings
file_type = pyedflib.FILETYPE_EDFPLUS
sfreq = mne_raw.info['sfreq']
date = datetime.now()#.strftime('%d %b %Y %H:%M:%S')
first_sample = int(sfreq * tmin)
last_sample = int(sfreq * tmax) if tmax is not None else None

# convert data
channels = mne_raw.get_data(picks,
start=first_sample,
stop=last_sample)

# convert to microvolts to scale up precision
#is_micro = np.median(channels, axis=1) < 1e-3

#channels *= 1e6

# set conversion parameters
dmin, dmax = [-32768, 32767]
pmin, pmax = [channels.min(), channels.max()]
n_channels = len(channels)

# create channel from this
try:
f = pyedflib.EdfWriter(fname,
n_channels=n_channels,
file_type=file_type)

channel_info = []
data_list = []

for i in range(n_channels):
if np.median(np.abs(channels[i])) < 1e-5:
scale = 1e9
unit = 'nV'
else:
if np.median(np.abs(channels[i])) < 1e-3:
scale = 1e6
unit = 'uV'
else:
scale = 1
unit = 'V'

ch_dict = {'label': mne_raw.ch_names[i],
'dimension': unit,
'sample_rate': sfreq,
'physical_min': pmin,
'physical_max': pmax,
'digital_min': dmin,
'digital_max': dmax,
'transducer': '',
'prefilter': ''}

channel_info.append(ch_dict)
data_list.append(scale*channels[i])

f.setTechnician('mne-gist-save-edf-skjerns')
f.setSignalHeaders(channel_info)
f.setStartdatetime(date)
f.writeSamples(data_list)
except Exception as e:
print(e)
return False
finally:
f.close()
return True
4 changes: 4 additions & 0 deletions tinnsleep/io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from tinnsleep.external.save_edf import write_edf

def fif2edf(file):
return False