[FieldTrip] Issue plotting Brainnetome aligned EEG source data
Jack Fogarty
jf752 at uowmail.edu.au
Thu Jul 1 14:10:46 CEST 2021
Hi Jan Mathijs,
Thanks for your consideration. I have some responses to some of your points below:
1. "It is a bit unclear to me what the relevance of the atlas is in all this."
Ultimately, I am looking to explore EEG connectivity between brainnetome ROIs. Specifically, in my full pipeline I am:
a) Creating Brainnetome atlas aligned template grid
b) Using the template grid for source reconstruction across all EEG trials (i.e. epochs)
c) Muliplying the spatial filter with the individual EEG epochs to get trial-level source data
d) Projecting the dipole moments to their max in each trial
e) Using ft_volumelookup to select and then average the dipole mom for each brainnetome ROI to create a 'virtual channel' timecourse
f) Running ft_freqanalysis and ft_connectivity analysis on the virtual channel data
2. "The atlas should not be used a priori to define the ‘inside’ compartment. You can map the dipoles onto the anatomical parcels after you performed the source reconstruction."
Is this still the case if I am using a standard BEM model? I decided to create a template grid pre-aligned to the Brainnetome atlas to make it simple to compare subject source-level data and interpret the outcomes relative to specific ROIs in the brainnetome atlas. Is this a poor approach?
I did first try the typical 'source reconstruction -> interpolation to atlas -> source parcellate' pipeline but I ran into a lot of problems trying to interpolate my source reconstruction onto the Brainnetome atlas. Further digging led me to the current approach as a possible solution.
3. "Following up on my previous message: I didn’t think of the fact that you are using EEG, with a BEM volume conduction model. In this case the inwardshift is probably not going to work."
You are right there, as far I can figure. I played a lot with various inwardshift, resolution, and other settings when I was preparing the template_grid (sourcemodel), and I also mucked around with ft_sourceplot projection methods. In some cases, certain options did look like they improved the surface plot but I think they were only hiding some of the problem. Inward shift only appeared to cover the surface when I had shifted many gridpoints 'outside' the brain volume; although, regardless, as soon as I align the template grid to the brainnetome atlas the problem returns.
4. "Is there a particular reason why you’d want to visualize your results on the surface?"
Self-gratification, perhaps. Really, I was hoping to plot the outcomes on a surface because it is a nice and simple way to display results, both for checking that things 'look sensible' and for potential publications where we might use this atlas. Is it the data type that makes this tricky? I am wondering if my pipeline is ok, but perhaps I need to give up on the surface plot, or create one specifically to represent data at ROIs of the Brainnetome atlas.
In case it is helpful for you, or for other interested readers, my full code for steps a-e is below. Thanks again for your thoughts!
Best,
Jack
%% Fieldtrip: eLORETA ROI timecourses
% https://www.fieldtriptoolbox.org/example/create_single-subject_grids_in_individual_head_space_that_are_all_aligned_in_brain_atlas_based_mni_space/
% https://www.fieldtriptoolbox.org/tutorial/salzburg/#reconstruct-the-time-course-of-activity-at-a-particular-brain-location
ft_defaults
[ftver, ftpath] = ft_version;
% Load the Headmodel
load(fullfile(ftpath, 'template/headmodel/standard_bem.mat')); % Colin27
% Prepare the template grid 'standard sourcemodel'
cfg = [];
cfg.xgrid = 'auto';
cfg.ygrid = 'auto';
cfg.zgrid = 'auto';
cfg.unit = 'mm';
cfg.tight = 'yes';
cfg.resolution = 7; % What does this result in regard to the final resolution for reporting?
cfg.headmodel = vol;
template_grid = ft_prepare_sourcemodel(cfg);
% Check alignment of the template grid and headmodel
figure;
hold on
ft_plot_mesh(template_grid.pos(template_grid.inside,:));
ft_plot_headmodel(vol, 'facecolor', 'cortex', 'edgecolor', 'none');
ft_plot_axes(vol);
alpha 0.5
camlight
% Read in Brainnetome Atlas; ensure its units are consistent with template
atlas = ft_read_atlas(fullfile(ftpath, 'template/atlas/brainnetome/BNA_MPM_thr25_1.25mm.nii'));
% Ensure units are consistent between atlas and sourcemodel
atlas = ft_convert_units(atlas,'mm');
template_grid = ft_convert_units(template_grid,'mm');
% Check the coordsys field exists in sourcemodel
if ~isfield(template_grid, 'coordsys');
template_grid.coordsys = 'mni';
end
% Create binary mask for all grid points inside the Brainnetome atlas locations
cfg = [];
cfg.atlas = atlas;
cfg.roi = atlas.tissuelabel; % here you can also specify a single label, i.e. single ROI
cfg.inputcoord = 'mni';
mask = ft_volumelookup(cfg, template_grid);
template_grid.inside = false(template_grid.dim);
template_grid.inside(mask == 1) = true;
% Check grid points inside the Brainnetome atlas
figure;
ft_plot_mesh(template_grid.pos(template_grid.inside,:),'vertexcolor','black','vertexalpha',0.5);
clearvars -except atlas template_grid vol
%% Prep EEG source analysis
setenv('PATH', 'C:\Program Files\OpenMEEG\bin');
setenv('LD_LIBRARY_PATH', 'C:\Program Files\OpenMEEG\lib');
% Load preprocessed EEG data
load('C:\Users\jf752\Desktop\Processing\source_analysis\test_data');
% Realign electrodes
cfg = [];
cfg.method = 'interactive';
cfg.headshape = vol.bnd(1);
cfg.elec = cln_data.elec;
elec_aligned = ft_electroderealign(cfg);
cln_data.elec = elec_aligned;
% Convert time for each trial so that they are equivalent (for rawtimelock)
for i = 1:length(cln_data.time);
cln_data.time{1,i} = 0:0.002:1.998;
end
clear i elec_aligned cfg
% Compute sensor-level time-locked data
cfg = [];
cfg.trials = 'all';
cfg.keeptrials = 'yes';
cfg.covariance = 'yes';
timlock = ft_timelockanalysis(cfg,cln_data);
% Prepare the leadfield
cfg = [];
cfg.elec = cln_data.elec;
cfg.channel = cln_data.label;
cfg.headmodel = vol; % volume conduction model
cfg.sourcemodel.pos = template_grid.pos; % number of voxels
leadfield = ft_prepare_leadfield(cfg);
clear cfg
%% Run source localisation and calculate single-trial ROI timecourses
% Compute eLORETA time-course based on the total data
cfg = [];
cfg.method = 'eloreta';
cfg.elec = timlock.elec;
cfg.sourcemodel = leadfield;
cfg.headmodel = vol;
cfg.projectnoise = 'yes';
cfg.keepmom = 'yes';
cfg.keepfilter = 'yes';
source = ft_sourceanalysis(cfg, timlock);
% Calculate 3D trial moms based on Michael Glassen's FTMailing Request
% https://mailman.science.ru.nl/pipermail/fieldtrip/2020-May/039989.html
for f = 1:length(source.avg.filter);
for t = 1:length(cln_data.trial);
if isempty(source.avg.filter{f});
x = [];
else
x = source.avg.filter{f}*cln_data.trial{t};
end
trial_source.trial(t).mom{f} = x;
end
end
% Mimic Source.avg config to project trial dipole moments in direction of maximal power
tmp = source;
for t = 1:length(cln_data.trial);
tmp.avg.mom = trial_source.trial(t).mom;
cfg = [];
cfg.projectmom = 'yes';
tmp = ft_sourcedescriptives(cfg, tmp);
trial_source.trial(t).mom = tmp.avg.mom;
trial_source.trial(t).pow = tmp.avg.pow;
trial_source.trial(t).ori = tmp.avg.ori;
trial_source.trial(t).label = tmp.avg.label;
trial_source.trial(t).filterdimord = tmp.avg.filterdimord;
end
clear f t x tmp ans cfg
% Calculate the average signal in each ROI
for t = 1:length(cln_data.trial);
for r = 1:length(atlas.tissuelabel);
cfg = [];
cfg.atlas = atlas;
cfg.roi = atlas.tissuelabel{1,r}; % here you can also specify a single label, i.e. single ROI
cfg.inputcoord = 'mni';
mask = ft_volumelookup(cfg, template_grid);
mask_idx = find(mask);
for m = 1:length(mask_idx);
if isempty(trial_source.trial(t).mom{mask_idx(m)});
tmp(m,:) = nan(1,length(cln_data.trial{1}));
else
tmp(m,:) = trial_source.trial(t).mom{mask_idx(m)};
end
end
roi(r,:) = nanmean(tmp);
clear tmp mask mask_idx ans m
end
roi_trial_dat{t} = roi;
clear roi r
end
More information about the fieldtrip
mailing list