Skip to content

Conversation

@adam2392
Copy link
Member

@adam2392 adam2392 commented Aug 25, 2021

Reference issue

Follow up fix from #9643 where not all the relevant "Voltage/uVoltage" channels were added.

In addition, it will allow channel types to be written to EDF files, in accordance with: #9694 (comment)

Closes: #9704
Closes: #9730

What does this implement/fix?

  • Adds corresponding unit test that makes sure all voltage channels are covered in exporting EDF
  • Allows channel types to be read in from EDF files via their signal labels
  • Allows exporting channel types to EDF files

@cbrnr
Copy link
Contributor

cbrnr commented Aug 25, 2021

The test doesn't cover the bio and dbs types you added here. Do we really want these types? The docstring says EDF export only supports EEG, ECoG, and sEEG, so this should be updated in any case (whether or not we add bio and dbs, because we already support eog, ecg, and emg).

@adam2392
Copy link
Member Author

The test doesn't cover the bio and dbs types you added here. Do we really want these types? The docstring says EDF export only supports EEG, ECoG, and sEEG, so this should be updated in any case (whether or not we add bio and dbs, because we already support eog, ecg, and emg).

Oh apologies, forgot to push up the updated test.

Upon further inspection, I realized that EDF cannot write the type of channels in the header, as there is nowhere to store that. I also realized that read_raw_edf will end up losing the channel type information. With that being said, one should be able to write any type of "voltage" type signals into the EDF file, as I have seen EOG, ECG and even EMG before. DBS and Bio are just future-proofing since it doesn't hurt to add them.

What we might need to do though is "specify" that you lose channel type information in the doc string.

However, with packages like mne-bids, this issue is circumvented since one would just leverage the channels.tsv file in BIDS to set channel types.

Copy link
Member

@agramfort agramfort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no objection to merge

@adam2392 adam2392 changed the title [MRG] Fixed minor bug where other types of channels were not written [WIP] Fixed minor bug where other types of channels were not written Aug 25, 2021
@adam2392
Copy link
Member Author

Okay now I think this is good to go from me. I put the hidden runtime bug that's user dependent into a new issue.

@adam2392 adam2392 changed the title [WIP] Fixed minor bug where other types of channels were not written [MRG] Fixed minor bug where other types of channels were not written Aug 25, 2021
@adam2392 adam2392 requested a review from larsoner August 26, 2021 14:07
@Teuniz
Copy link

Teuniz commented Aug 30, 2021

Upon further inspection, I realized that EDF cannot write the type of channels in the header, as there is nowhere to store that.

There is:

The header field 'label' offers 16 ASCII characters. The standard structure consists of three components, from left to right:

Type of signal (for example EEG).  
A space.  
Specification of the sensor (for example Fpz-Cz).  

https://edfplus.info/specs/edftexts.html#label

https://edfplus.info/specs/edftexts.html#signals

@adam2392 adam2392 changed the title [MRG] Fixed minor bug where other types of channels were not written [MRG, BUG] Fixed bug with EDF channel types and minor bug where other types of channels were not written Aug 30, 2021
@adam2392 adam2392 changed the title [MRG, BUG] Fixed bug with EDF channel types and minor bug where other types of channels were not written [WIP, BUG] Fixed bug with EDF channel types and minor bug where other types of channels were not written Aug 30, 2021
@adam2392 adam2392 requested a review from agramfort August 31, 2021 03:33
@adam2392
Copy link
Member Author

Sorry @agramfort there were some updates pertaining to how EDF should be read/written.

This is good to go again on my end, and I have added in all the thoughts from #9704

Copy link
Member

@agramfort agramfort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does eeglab do when reading files with ch types like this?

@cbrnr
Copy link
Contributor

cbrnr commented Aug 31, 2021

what does eeglab do when reading files with ch types like this?

I assume that readers that do not parse the label field will just display the complete text as a channel label, e.g. "EEG Fz" instead of just "Fz". For this reason, I would like to have an option to turn channel type parsing off.

@adam2392 adam2392 requested review from agramfort and cbrnr September 1, 2021 13:52
@adam2392
Copy link
Member Author

mne/tests/test_cov.py::test_compute_whitener seems to fail, but I don't think I made any changes affecting that.

Co-authored-by: Clemens Brunner <clemens.brunner@gmail.com>
@cbrnr
Copy link
Contributor

cbrnr commented Sep 14, 2021

I'm sorry if you have already mentioned it in this long thread, but how does EDF only support voltage data? The specs just mention that the physical dimension for each channel are 8 ASCII characters, so any unit should be supported.

@adam2392
Copy link
Member Author

I'm sorry if you have already mentioned it in this long thread, but how does EDF only support voltage data? The specs just mention that the physical dimension for each channel are 8 ASCII characters, so any unit should be supported.

Idk actually. I think possibly we could export any channel type, but I would prefer not implementing the ability to export MEG -> EDF. My impression was that EDF should only be used for EEG-like data. I don't see why anyone would export MEG/non-voltage data to EDF and not FIF unless it was by accident, so we have some catches in export to handle that.

Ref: https://www.edfplus.info/specs/edftexts.html

@cbrnr
Copy link
Contributor

cbrnr commented Sep 14, 2021

I disagree. The table on the specs page even lists MEG as a signal type. I think we should not artificially restrict our export functionality by allowing only voltage data, which is not backed up by the EDF specs.

@cbrnr
Copy link
Contributor

cbrnr commented Sep 14, 2021

Re exporting to EDF vs. FIFF, I think EDF is much more widely adopted/used than FIFF, but even if FIFF is the de facto standard for MEG signals the EDF format allows signals of any unit to be stored.

phys_dims = 'uV'

# get EEG-related data in uV
units = dict(eeg='uV', ecog='uV', seeg='uV', eog='uV', ecg='uV', emg='uV',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cbrnr can you lmk which data channels you would like included here? I'm not a MEG user, so I've only ever used MNE for EEG/iEEG.

Would it be:

mag='fT', grad='fT/cm'

Anything else?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would include everything. Why not?

Copy link
Member Author

@adam2392 adam2392 Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the challenge is more-so on reading in MEG/fnirs data correctly using read_raw_edf. I'm not sure how to read in this data correctly. Sure I can easily append "MEG M1" to indicate M1 is a MEG channel, or "FNIRS M2" to indicate M2 is a FNIRS channel, but reading the data back in to do a unit test on the round trip is non-trivial for me.

Is it possible to leave this PR as is and then table it for a future improvement from someone who wants to export MEG/Fnirs to EDF and is familiar with the reader of MEG/Fnirs enough to add that functionality in?

Reference LOC:

for idx, ch_name in enumerate(ch_names):
chan_info = {}
chan_info['cal'] = 1.
chan_info['logno'] = idx + 1
chan_info['scanno'] = idx + 1
chan_info['range'] = 1.
chan_info['unit_mul'] = FIFF.FIFF_UNITM_NONE
chan_info['ch_name'] = ch_name
chan_info['unit'] = FIFF.FIFF_UNIT_V
chan_info['coord_frame'] = FIFF.FIFFV_COORD_HEAD
chan_info['coil_type'] = FIFF.FIFFV_COIL_EEG
chan_info['kind'] = FIFF.FIFFV_EEG_CH
chan_info['loc'] = np.zeros(12)
if ch_name in eog or idx in eog or idx - nchan in eog:
chan_info['coil_type'] = FIFF.FIFFV_COIL_NONE
chan_info['kind'] = FIFF.FIFFV_EOG_CH
pick_mask[idx] = False
elif ch_name in misc or idx in misc or idx - nchan in misc:
chan_info['coil_type'] = FIFF.FIFFV_COIL_NONE
chan_info['kind'] = FIFF.FIFFV_MISC_CH
pick_mask[idx] = False
elif idx in stim_channel_idxs:
chan_info['coil_type'] = FIFF.FIFFV_COIL_NONE
chan_info['unit'] = FIFF.FIFF_UNIT_NONE
chan_info['kind'] = FIFF.FIFFV_STIM_CH
pick_mask[idx] = False
chan_info['ch_name'] = ch_name
ch_names[idx] = chan_info['ch_name']
edf_info['units'][idx] = 1
chs.append(chan_info)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for doing it later. I assume YAGNI, and for MEG important information will be lost anyway (at the very least, the head<->device transform, sensor orientations, sensor coil types)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same could be said for EEG as well because EDF doesn't store custom sensor locations. But let's stick to exporting EEG for now, the warning message and code comment are now clear that this is not a restriction imposed by EDF.

@adam2392
Copy link
Member Author

I disagree. The table on the specs page even lists MEG as a signal type. I think we should not artificially restrict our export functionality by allowing only voltage data, which is not backed up by the EDF specs.

Okay sure I can add in then MEG support. Can you lmk which channels and their corresponding default units I should add in to this comment? I'm not too familiar with non-EEG data types in MNE.

#9694 (comment)

@adam2392
Copy link
Member Author

@adam2392
Copy link
Member Author

Don't want to lose track of this PR since it had a lot of discussion.

Is this good to merge now?

@agramfort agramfort merged commit a2036aa into mne-tools:main Sep 15, 2021
@agramfort
Copy link
Member

thx @adam2392

chan_info['coil_type'] = FIFF.FIFFV_COIL_EEG
chan_info['kind'] = FIFF.FIFFV_EEG_CH
chan_info['loc'] = np.zeros(12)
chan_info['loc'] = nan_arr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needed to be nan_arr.copy() (as found with @drammock working together), after this PR all info['chs'][ii]['loc'] point to the same array in memory, so setting values in one of them sets all of them. We need a unit test that would have caught this...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think @drammock is working on a fix for this)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah my bad. I changed to nan to better match roundtrip.

Lmk if you need me to PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #9743

hoechenberger pushed a commit to hoechenberger/mne-python that referenced this pull request Sep 17, 2021
… types of channels were not written and bug where annotations were not written to correct scale (mne-tools#9694)

* Fixing bug

* Update doc.

* Update docstring

* Add diff

* Add diff

* Add diff

* Add warning

* Fix flake

* Adding test at data level.

* Dont mess with global random state

* Adding note to edf export

* Adding note to edf export

* Adding updated unit tests and io reading for edf

* Adding unit test for channel type

* Adding change log

* Add warning

* Add additional parameter

* Fix docstring

* Fixing other test

* Fix flake

* Apply suggestions from code review

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>

* Update mne/export/tests/test_export.py

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>

* Fixing flake

* Apply suggestions from code review

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Update mne/export/_edf.py

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Update mne/export/_edf.py

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Update mne/export/_edf.py

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Merge

* Address comments

* Check double export

* Fix flake

* Remove scruff

* Fix codespell

* Apply suggestions from code review

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* update docstring

* Commeent on stim

* Fix annotations

* Adding docstring

* Fix number of annotations

* Add check for duration

* Fix issues

* Fix docstring

* Apply suggestions from code review

Co-authored-by: Clemens Brunner <clemens.brunner@gmail.com>

* Clarify warning and inline comment

* Fix docstring

* Fix doc warning and len(annotations).

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>
Co-authored-by: Daniel McCloy <dan@mccloy.info>
Co-authored-by: Clemens Brunner <clemens.brunner@gmail.com>
agramfort added a commit that referenced this pull request Oct 7, 2021
* Pull in changes from old PR

* [MRG, BUG] Fixed bug with EDF channel types and minor bug where other types of channels were not written and bug where annotations were not written to correct scale (#9694)

* Fixing bug

* Update doc.

* Update docstring

* Add diff

* Add diff

* Add diff

* Add warning

* Fix flake

* Adding test at data level.

* Dont mess with global random state

* Adding note to edf export

* Adding note to edf export

* Adding updated unit tests and io reading for edf

* Adding unit test for channel type

* Adding change log

* Add warning

* Add additional parameter

* Fix docstring

* Fixing other test

* Fix flake

* Apply suggestions from code review

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>

* Update mne/export/tests/test_export.py

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>

* Fixing flake

* Apply suggestions from code review

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Update mne/export/_edf.py

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Update mne/export/_edf.py

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Update mne/export/_edf.py

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* Merge

* Address comments

* Check double export

* Fix flake

* Remove scruff

* Fix codespell

* Apply suggestions from code review

Co-authored-by: Daniel McCloy <dan@mccloy.info>

* update docstring

* Commeent on stim

* Fix annotations

* Adding docstring

* Fix number of annotations

* Add check for duration

* Fix issues

* Fix docstring

* Apply suggestions from code review

Co-authored-by: Clemens Brunner <clemens.brunner@gmail.com>

* Clarify warning and inline comment

* Fix docstring

* Fix doc warning and len(annotations).

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>
Co-authored-by: Daniel McCloy <dan@mccloy.info>
Co-authored-by: Clemens Brunner <clemens.brunner@gmail.com>

* Remove _BEM_VIEWS from __init__.py

* Add back cropped MNE logo

* Change default of raw_psd back to False again

* Remove old JS libraries

* FIX _repr_html_ of Info add all channel types (#9725)

* update info html template

* add all channel types to the html repr

* update html repr test

* Update mne/io/meas_info.py

Co-authored-by: Eric Larson <larson.eric.d@gmail.com>

* Update mne/io/meas_info.py

Co-authored-by: Eric Larson <larson.eric.d@gmail.com>

* add repr_html smoke test

* ENH: Titles

* update changelog

* fix name in the change log

* FIX: Move down

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>
Co-authored-by: Eric Larson <larson.eric.d@gmail.com>

* Don't call tight_layout() on figs passed to _fig_to_img()

* Reduce verbosity related to prjector handling

* Don't produce empty drop figure if no epochs were dropped

* Don't add legend to GFP plots

* use viz.utils.tight_layout()

* More tight_layout()

* Fix warnings

* Finally tight_layout() issues 🙈

* Add back time zone test

* Deprecation handling

* Better deprecation testing

* ALMOST finish work on tutorial

* Disable carousel buttons

* Finalize tutorial

* Maximize main content column width

* Allow more than 1 BEM sliders in a row

* Apply changes discussed with Alex

* flake

* Add options to decimate time points in STC and Evoked plots

* Add time decimation params to parse_folder()

* Try to speed up tutorial rendering

* Allow further decimation, render STCs in parse_folder()

* Style and further speedups

* Even more speeeedd 🚗💨💨

* flake

* Apply suggestions from code review [ci skip]

Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>

* Fix reference

* Reduce processing time

* Further reduce tutorial runtime [skip azp][skip actions]

* Explain butterfly param

* Avoid "deprecated" decorator

* Re-enable add_sys_info test

* Don't show STC traces, fix layout

* Remove some cruft

* Add changelog entries

* Remove duplicate line in changelog [ci skip]

* A few tiny layout tweaks

* Hack around issue between deprecated decorator and numpydoc

* Better Note for add_code

* Revert "Hack around issue between deprecated decorator and numpydoc"

This reverts commit 6d8f9ac.

* ENH: Better decorator

* BUG: Dont force tight_layout

* DOC: Better linking

* FIX: Ori labels

* STY: Flake

* FIX: Missed one

* TST: "Fix" doctest

* FIX: One more

* Use docdict for some more subject and subjects_dir params

* Explain slices=None and reformat code for readability

* Correct STC subject docstrings

* Fix: plot_bem() requires subject param

* Remove whitespace

* Properly encode titles and TOC entries

* Center SVGs, fix horizontal scrolling issue

* Add fill_doc decorator to plot_bem()

* Fix tests

* ENH: Speed up test

* Speed up evoked rendering in test

* MAINT: Speed up tests

* FIX: Also mayavi

Co-authored-by: Adam Li <adam2392@gmail.com>
Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>
Co-authored-by: Daniel McCloy <dan@mccloy.info>
Co-authored-by: Clemens Brunner <clemens.brunner@gmail.com>
Co-authored-by: Valerii <42982039+vagechirkov@users.noreply.github.com>
Co-authored-by: Eric Larson <larson.eric.d@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Raw annotations incorrectly exported to EDF Improving EDF reading in line with specification

6 participants