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

Nihon Kohden EEG file reader WIP #6017

Merged
merged 17 commits into from
Sep 9, 2020

Conversation

fraimondo
Copy link
Contributor

This is a reader module for Nihon Kohden native EEG file format.

It is a WIP, so for now it does not read events, no metadata and the channel names are the standard ones (it can be renamed through a file).

I still need to add a test.

I leave it here so we can start doing the collaborative stuff and if anyone has any interest, they can chip in.

@larsoner
Copy link
Member

larsoner commented Mar 2, 2019

This pull request introduces 1 alert when merging 4b922d8 into ce50c2b - view on LGTM.com

new alerts:

  • 1 for Unused local variable

Comment posted by LGTM.com

@larsoner
Copy link
Member

@fraimondo can you get some tiny file in mne-testing-data? I don't have any Nihon Koden data, but once that testing file is there, I (and others) can help with this.

@larsoner larsoner added the Core label Mar 27, 2019
@fraimondo
Copy link
Contributor Author

fraimondo commented Apr 2, 2019 via email

@fraimondo
Copy link
Contributor Author

fraimondo commented May 9, 2019 via email

@larsoner
Copy link
Member

@fraimondo any time to come back to this in the next couple of weeks to make it into 0.21? You now have your testing file in mne-testing-data.

If not, okay for me to push some commits to get tests working?

@fraimondo
Copy link
Contributor Author

Just updated the code. This works. Can be improved by reading the annotations and more metadata.

@fraimondo
Copy link
Contributor Author

There's an issue with the channel names. Hold the PR.

@larsoner
Copy link
Member

larsoner commented Sep 3, 2020

Just updated the code. This works. Can be improved by reading the annotations and more metadata.

Awesome, I was just about to look into this to see if we could get into usable shape in the next week to make it into 0.21. Let me know if you need help

@larsoner larsoner added this to the 0.21 milestone Sep 3, 2020
@fraimondo
Copy link
Contributor Author

fraimondo commented Sep 7, 2020

Can't figure out this one. I'm getting None as the cals on the _read_segment_file function. I guess it's related to this commit: 06f232c

So now I use the _mult_cal_one function. But it still fails. Apparently the length of the idx parameter does not match data.

It gives me an error that it can't broadcast the data after _mult_cal_one. So here's the issue:

> /Users/fraimondo/dev/mne-python/mne/io/nihon/nihon.py(394)_read_segment_file()
    392         import pdb; pdb.set_trace()
    393         # block_data = ((block_data[idx, :] * cals + offsets) * gains)
--> 394         _mult_cal_one(data, block_data, idx, cals, mult)
    395         data[:, :] += offsets
    396         data[:, :] *= gains

ipdb> data.shape                                                                                                                        
(1, 5800)
ipdb> idx.shape                                                                                                                         
(24,)

any clue?

The problem with this kind of files is that the data needs to be multiplied by the cals, then add offset and finally multiply by units (gains).

@larsoner
Copy link
Member

larsoner commented Sep 7, 2020

I recently made some changes to this function. Okay for me to look and push a commit tomorrow?

@fraimondo
Copy link
Contributor Author

fraimondo commented Sep 7, 2020 via email

@larsoner
Copy link
Member

larsoner commented Sep 7, 2020

Setting cals=1 should be fine. The edf reader was updated to behave this way.

Basically the second arg of _mult_cal_one (one) should always have shape (n_data_channels, n_times) (and n_times can be shorter if you use it in chunks / with time-based slicing) then the _mult_cal_one will subindex the idx from n_data_channels that it needs. It's a bit wacky but it should work. The idx is really there just to tell you which rows of the one you actually need to fill / will be used.

@fraimondo
Copy link
Contributor Author

fraimondo commented Sep 7, 2020 via email

@larsoner
Copy link
Member

larsoner commented Sep 7, 2020

But when a projector is applied while picking channels (as in the _test_raw_reader function), the mult parameter has shape (n_proj, n_channels). So the way I was offsetting and multiplying by units does not work, since data will have shape (n_proj, n_times).

You should not need to worry about the shape of mult EDIT: or the shape of data. If you make sure one has shape (n_channels, n_times), internally _mult_cal_one will subselect the correct channels after projection (this actually happens just by mult @ one, more or less). So your job as the reader-writer is to make sure one is of shape (n_channels, n_times) with the idx indices populated. Then everything should work. You shouldn't ever use cals yourself, and you should make sure that you know that they will be multiplied into the data values. So if your pattern is one = (one * a + b) * c as above, you can either set cals to 1. for all channels in info['ch'][ii]['cal'], or you can set them to c in the expression above and only take care of the one = (one * a + b) part of the expression (and let _mult_cal_one do the cals mult for you). What you don't want is it happening in both places.

@agramfort
Copy link
Member

CIs complain @fraimondo

maybe rebase on master to fix it?

@fraimondo
Copy link
Contributor Author

Codecov is not happy with the coverage (80%). It's just a few lines testing for proper headers in the files / right values in the right places. I don't really know how to test those lines.

The other two are unrelated to this PR:

================================== FAILURES ===================================
_________ test_make_inverse_operator_diag[testing_data-testing_data] __________
mne\minimum_norm\tests\test_inverse.py:749: in test_make_inverse_operator_diag
    _compare_inverses_approx(inverse_operator_diag, inv_op, evoked,
mne\minimum_norm\tests\test_inverse.py:183: in _compare_inverses_approx
    assert corr > ctol
E   assert 0.7766994175372354 > 0.99

@larsoner
Copy link
Member

larsoner commented Sep 8, 2020

@fraimondo let me know when you're done and I'll see if I can simplify the _mult_cal_one stuff. I also see that info['chs'][ii]['unit'] is being used in a way that it shouldn't -- I'll add a check for that and fix it in this PR

@fraimondo
Copy link
Contributor Author

I'm fine now. I think I got enough codecov. At least I don't see a failed test.

# for x in range(len(raw.ch_names))]
# edf_ch_types = [types_dict[raw_edf.info['chs'][x]['kind']]
# for x in range(len(raw_edf.ch_names))]
# assert ch_types == edf_ch_types
Copy link
Member

Choose a reason for hiding this comment

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

cleanup?

@larsoner
Copy link
Member

larsoner commented Sep 8, 2020

@fraimondo pushed a commit to simplify. Feel free to address @agramfort's remaining comment then hopefully we're good to go!

@agramfort agramfort merged commit 40f3fa0 into mne-tools:master Sep 9, 2020
@agramfort
Copy link
Member

Thx @fraimondo

larsoner added a commit to libertyh/mne-python that referenced this pull request Sep 9, 2020
* upstream/master: (489 commits)
  MRG, DOC: Fix ICA docstring, add whitening (mne-tools#8227)
  MRG: Extract measurement date and age for NIRX files (mne-tools#7891)
  Nihon Kohden EEG file reader WIP (mne-tools#6017)
  BUG: Fix scaling for src_mri_t in coreg (mne-tools#8223)
  MRG: Set pyvista as default 3d backend (mne-tools#8220)
  MRG: Recreate our helmet graphic (mne-tools#8116)
  [MRG] Adding get_montage for montage to BaseRaw objects (mne-tools#7667)
  ENH: Allow setting tqdm backend (mne-tools#8177)
  [MRG, IO] Persyst reader into Raw object (mne-tools#8176)
  MRG, BUG: Fix errors in IO/loading/projectors (mne-tools#8210)
  MAINT: vectorize _read_annotations_edf (mne-tools#8214)
  FIX : events_from_annotation when annotations.orig_time is None and f… (mne-tools#8209)
  FIX: do not project to sphere; DOC - explain how to get EEGLAB-like topoplots (mne-tools#7455)
  [MRG, DOC] Added linear algebra of transform to doc (mne-tools#7087)
  FIX: Travis failure on python3.8.1 (mne-tools#8207)
  BF: String formatting in exception message (mne-tools#8206)
  BUG: Fix STC limit bug (mne-tools#8202)
  MRG, DOC: fix ica tutorial (mne-tools#8175)
  CSP component order selection (mne-tools#8151)
  MRG, ENH: Add on_missing to plot_events (mne-tools#8198)
  ...
marsipu pushed a commit to marsipu/mne-python that referenced this pull request Oct 14, 2020
* Supports reading Nihon Kohden EEG files

* Read channels from 21E file if available

* Fixing codestyle

* Fix in the Units

* Reads annotations. Ready to merge maybe?

* Fixed merge error

* Flake

* Add RawNihon to doc ignore

* use _mult_cal_one

* Flake

* Fixed reading cals/mult/offset/gains

* Spell check

* Try to increase coverage

* Try to increase coverage

* FIX: More standard

* Update mne/io/nihon/nihon.py

* Cleanup, ready to go

Co-authored-by: Eric Larson <[email protected]>
@ftadel
Copy link

ftadel commented Jan 6, 2021

Hello,

A colleague posted a NK .EEG file I don't know how to read:

In case you have interesting sources of information about the NK file formats...

Thanks!
François

@agramfort
Copy link
Member

MNE Python complains too:

In [3]: import mne
   ...:
   ...: raw = mne.io.read_raw_nihon('resting.eeg')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-c17d6c9c0e8b> in <module>
      1 import mne
      2
----> 3 raw = mne.io.read_raw_nihon('resting.eeg')

~/work/src/mne-python/mne/io/nihon/nihon.py in read_raw_nihon(fname, preload, verbose)
     44     mne.io.Raw : Documentation of attribute and methods.
     45     """
---> 46     return RawNihon(fname, preload, verbose)
     47
     48

<decorator-gen-224> in __init__(self, fname, preload, verbose)

~/work/src/mne-python/mne/io/nihon/nihon.py in __init__(self, fname, preload, verbose)
    313         logger.info('Loading %s' % data_name)
    314
--> 315         header = _read_nihon_header(fname)
    316         metadata = _read_nihon_metadata(fname)
    317

~/work/src/mne-python/mne/io/nihon/nihon.py in _read_nihon_header(fname)
    134         version = np.fromfile(fid, '|S16', 1).astype('U16')[0]
    135         if version not in _valid_headers:
--> 136             raise ValueError(
    137                 'Not a valid Nihon Kohden EEG file ({})'.format(version))
    138

ValueError: Not a valid Nihon Kohden EEG file (BFA NKC04/01/16)

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.

4 participants