Back to the main page.

Bug 3343 - ft_senstype, and ft_senslabel in particular, do not work reliably on iEEG data

Reported 2017-09-02 06:06:00 +0200
Modified 2019-08-10 12:41:08 +0200
Product: FieldTrip
Component: core
Version: unspecified
Hardware: PC
Operating System: Mac OS
Importance: P5 normal
Assigned to: Robert Oostenveld
Depends on:
See also:

Arjen Stolk - 2017-09-02 06:06:19 +0200

The issue is due to widely variable nature of iEEG and naming schemes. Below are reconstructed examples of electrode labels that caused issues when, for instance, trying to append different electrode structures. ft_appendsens does not allow doing so given that it found that not all iEEG electrode groups have the same 'eeg' senstype. It also causes issues with ft_plotting_sens, which will try to plot coils when it believes to have detected a 'ctf' senstype. elec.elecpos = randn(3,2); elec.chanpos = elec.elecpos; elec.label = {'AF1';'AF2'}; ft_senstype(elec) ans = eeg1010 elec.elecpos = randn(3,2); elec.chanpos = elec.elecpos; elec.label = {'BR1';'BR2'}; ft_senstype(elec) ans = eeg1010 BR and AF were actually occurring electrode names. Since we do not have control over the electrode naming with iEEG (this is determined by the medical institutes where the iEEG recordings take place), would it be somehow possible to make ft_senslabel more strict regarding other recording types, i.e. the eeg1010 and ctf systems?

Arjen Stolk - 2017-09-02 06:08:14 +0200

Made a copy&paste error. For the second replication, it should read: elec.elecpos = randn(3,2); elec.chanpos = elec.elecpos; elec.label = {'BR1';'BR2'}; ft_senstype(elec) ans = ctf

Jan-Mathijs Schoffelen - 2017-09-02 07:42:06 +0200

not inhibited by any deep thinking at this 'early' hour, but does adding a chantype to the array help? do we actually have an 'ieeg' chantype?

Arjen Stolk - 2017-09-02 07:57:45 +0200

We don't have an ieeg chantype (nor ecog or seeg), but from what I've seen no iEEG data ever came with this information or perhaps our low-level reading functions do not extract it. But we could have ft_electrodeplacement attach a chantype? If so, we would need to add support to functions the like of ft_plot_sens. Somewhat related, .besa file formats have the ability to carry chantype information but I have never seen this information specified in any data. Just in case, I added support as below. Note I had to distinguish between 'ecog' and 'ieeg', but it'd be technically also appropriate to make CORTICALGRID being saved out as 'ieeg'. data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_TRIGGER]==1) = {'trigger'}; % test data did not have any of the below set to 1 data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_CORTICALGRID]==1) = {'ecog'}; data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_INTRACRANIAL]==1) = {'ieeg'}; data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_SCALPELECTRODE]==1) = {'eeg'}; data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_MAGNETOMETER]==1) = {'megmag'}; data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_AXIAL_GRADIOMETER]==1) = {'megaxial'}; data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_PLANAR_GRADIOMETER]==1) = {'megplanar'}; data.chantype([data.orig.channel_info.channel_states(:).BSA_CHANTYPE_MEGREFERENCE]==1) = {'megref'};

Arjen Stolk - 2017-09-02 18:57:37 +0200

p.s. it seems chantype does not resolve the issue, unfortunately. elec.elecpos = randn(3,2); elec.chanpos = elec.elecpos; elec.label = {'BR1';'BR2'}; elec.chantype = {'eeg';'eeg'} elec = elecpos: [3x2 double] chanpos: [3x2 double] label: {2x1 cell} chantype: {2x1 cell} >> ft_senstype(elec) ans = ctf

Arjen Stolk - 2017-09-14 18:57:03 +0200

elec.elecpos = randn(2,3); elec.chanpos = elec.elecpos; elec.label = {'BR1';'BR2'}; elec.chantype = {'eeg';'eeg'} >> ft_datatype_sens(elec) ans = balance: [1x1 struct] chanpos: [2x3 double] chantype: {2x1 cell} chanunit: {2x1 cell} elecpos: [2x3 double] label: {2x1 cell} type: 'ctf' unit: 'dm' which, at line 151 of ft_datatype_sens, returns true for: ismeg = ft_senstype(sens, 'meg'); namely: ft_senstype(elec) ans = ctf Possible solution #1: avoid calling ft_senstype in ft_dataype_sens when there already is a chantype (we can make ft_electrodeplacement spit out chantype = 'eeg') Possible solution #2: a more clean fix would be to somehow avoid having ft_senstype being so easily convinced it's a MEG senstype (or a EEG cap for that matter). to see this gullibility in action: elec.elecpos = randn(8,3); elec.chanpos = elec.elecpos; elec.label = {'1';'2';'3';'4';'5';'6';'7';'8'}; >> ft_senstype(elec) ans = eeg > score: great elec.elecpos = randn(9,3); elec.chanpos = elec.elecpos; elec.label = {'1';'2';'3';'4';'5';'6';'7';'8';'BR1'}; ft_senstype(elec) ans = ctf > score: fail if the BR1 were an iEEG electrode label elec.elecpos = randn(9,3); elec.chanpos = elec.elecpos; elec.label = {'1';'2';'3';'4';'5';'6';'7';'8';'AF1'}; ft_senstype(elec) ans = eeg > surprising pass! I was expecting this to incorrectly say eeg1010, which it will do (correctly) in case there are only electrodes belonging to the EEG1010 system: elec.elecpos = randn(2,3); elec.chanpos = elec.elecpos; elec.label = {'AF1';'AF2'}; ft_senstype(elec) ans = eeg1010 So, this last toy example maybe narrows the solution to check for all electrodes combined for the CTF case as well? That is, if there's something in there (or maybe a large portion) that doesn't fit the MEG dewar specific description, maybe not call it ctf? The toughest scenario from the iEEG side from be it a single depth shaft with labels BR1, BR2, BR10. ft_senstype (ft_senslabel actually) would have to recognize that say BR1 and BR2 are ctf, but maybe the others aren't. Here, still it would helpful if ft_senstype could also take into the equation an already specified chantype field. Just for the global picture, I feel this issue is going to grow into a pain as several people have already encountered it in my direct environment.

Arjen Stolk - 2017-09-14 19:12:04 +0200

Possible solution #3: Related to #1, we create a new chantype 'ieeg' that will be a no-go area for ft_senstype. iEEG sens data is always created through ft_electrodeplacement or imported from bioimage suite using the bis2fieldtrip conversion tool. We could make both append a chantype = 'ieeg'. The only problem I can currently think of is that when import their data manually (i.e. matlab fiddling), that the chantype field will be a required format (if they, for instance, want to use appendsens for appending it to 'normally' imported iEEG sens data). Any thoughts, RO, JM, RvdM?

Robert Oostenveld - 2017-09-15 09:27:41 +0200

(In reply to Arjen Stolk from comment #6) I see on line 393 in ft_senstype for the "last resort" attempts when islabel that for eeg1005 it already requires that there are >10. The same could be done for btiref and ctfref (with an appropriate number, say 50% of the expected channels. Furthermore, it does not use chantype in any way. I have now added chantype handling in case it defaults to 'eeg'. The rationale now is that seeg and ecog are both considered ieeg seeg, ecog and ieeg are all considered eeg And it returns the most specific category that applies. mac011> git push --set-upstream origin bug3343-chantype Counting objects: 147, done. Delta compression using up to 4 threads. Compressing objects: 100% (134/134), done. Writing objects: 100% (147/147), 37.34 KiB | 0 bytes/s, done. Total 147 (delta 104), reused 23 (delta 13) remote: Resolving deltas: 100% (104/104), completed with 46 local objects. To * [new branch] bug3343-chantype -> bug3343-chantype Branch bug3343-chantype set up to track remote branch bug3343-chantype from origin.

Robert Oostenveld - 2017-09-15 09:29:22 +0200

I have merged Arjen, you may want to check whether the test script matches your expectations.

Arjen Stolk - 2017-09-15 18:02:07 +0200

This looks great, at first sight. Will do some more testing later. Shall I make ft_electroplacement append an ieeg chantype (and probably bis2ftieldtrip too)?

Robert Oostenveld - 2017-09-21 18:33:10 +0200

(In reply to Arjen Stolk from comment #9) ft_electrodeplacement is not only for ECoG and sEEG, also for regular EEG. I propose cfg.chantype = string where it defaults to ieeg for method='mri' and eeg for method='headshape' and '1020'. When specified as empty by the researcher (i.e. emptyismeaningfull in ft_getopt), the chantype should not be added at the end of the function. Otherwise the cfg.chantype can be duplicated for all channels.

Arjen Stolk - 2017-09-21 18:38:59 +0200

The function already pivots already around headshape vs. volume input. Is the latter always going to be ieeg (I guess so), and the former eeg (I guess not)? If true, cfg.chantype would be obsolete. Otherwise, what to set for a default? Alternatively, I wonder whether we still need this since I think your adjustments solved the issue (haven't been able to test rigorously yet). That is, a call to ft_sens_data (I forgot the exact name) may lead to a complete data structure (though, it that fails all localization effort is wasted, so better not at the end of the function).

Robert Oostenveld - 2017-09-21 18:44:07 +0200

(In reply to Arjen Stolk from comment #11) no, MRI can also be used for localizing EEG electrodes (although it hardly ever happens). If you do combined EEG/fMRI you get the scalp electrode "artifacts/locations" for free in the anatomical MRI. I should ask Rene for an example. If you don't need it any more, please don't bother. I see the chantype mainly as decoration of the elec structure, not as a necessity.

Arjen Stolk - 2017-09-21 18:48:39 +0200

Ok, let's call this a day then. I might resurrect the issue if I do find we need chantype at some point, but for now not kicking a dead horse.

Roemer van der Meij - 2017-09-21 22:10:10 +0200

Joining the party late as I was holidaying where Irma the hurricane didn't end up luckily, but I don't have any insights to add. In similar spirit I struggled with this in ft_prepare_layout too, where the automatic detection also ended up with EEG/MEG type layouts. The auto-detection is a "problem" that will only get worse in the future since the number of supported data types will likely increase (non-cooled MEG sensors? ???). From that perspective it is probably wise to keep the status of the chantype field as decorative? If it at some point needs to become more influential, it is possible best to take an conservative approach, and default quickly to 'unknown' if there's ambiguity.

Robert Oostenveld - 2019-08-10 12:34:58 +0200

This closes a whole series of bugs that have been resolved (either FIXED/WONTFIX/INVALID) for quite some time. If you disagree, please file a new issue on

Robert Oostenveld - 2019-08-10 12:41:08 +0200

This closes a whole series of bugs that have been resolved (either FIXED/WONTFIX/INVALID) for quite some time. If you disagree, please file a new issue on