FieldTrip beamformer demo

In this demonstration we will use the face recognition dataset.

Please use the general instructions to get started.

Part 1 - coregistration and head model construction

%% get data from SPM

D = spm_eeg_load('../spm-source-demo/PapMcbdspmeeg_run_01_sss.mat');

% convert sensors and volume conduction model from SPM
volsens = spm_eeg_inv_get_vol_sens(D, 1, 'Head', 'inv', 'MEG');
vol1    = volsens.MEG.vol;
sens1   = volsens.MEG.sens;
mri1    = ft_read_mri('../spm-source-demo/mprage.nii');

%% start from scratch data in FieldTrip

subj = 15;
prefix = sprintf('Sub%02d', subj);
load([prefix '_raw']);  % this is called "data" rather than "raw"

sens = data.grad;

% load the original MRI
mri_orig = ft_read_mri('../data/Sub15/T1/mprage.nii');

% load the positions of the anatomical fiducials (as provided by Rik)
load('../data/Sub15/T1/mri_fids.mat');

headshape = ft_read_headshape('../data/Sub15/MEEG/run_01_raw.fif');
headshape = ft_convert_units(headshape, 'mm');

% the MRI is neither expressed in MNI, nor in Neuromag coordinates
ft_determine_coordsys(mri_orig, 'interactive', 'no');
hold on; % add the subsequent objects to the same figure
ft_plot_headshape(headshape);
plot3(mri_fids(1,1), mri_fids(1,2), mri_fids(1,3), 'm*');
plot3(mri_fids(2,1), mri_fids(2,2), mri_fids(2,3), 'm*');
plot3(mri_fids(3,1), mri_fids(3,2), mri_fids(3,3), 'm*');

%% validate the positions of the fiducials that were provided by Rik

cfg = [];
cfg.location = mri_fids(1,:);
ft_sourceplot(cfg, mri_orig);

cfg = [];
cfg.location = mri_fids(2,:);
ft_sourceplot(cfg, mri_orig);

cfg = [];
cfg.location = mri_fids(3,:);
ft_sourceplot(cfg, mri_orig);

%%

% the location of fiducials is expressed in original MRI coordinates
% ft_volumerealign needs them in voxel coordinates
vox_fids = ft_warp_apply(inv(mri_orig.transform), mri_fids);

cfg = [];
cfg.fiducial.nas = vox_fids(1,:);
cfg.fiducial.lpa = vox_fids(2,:);
cfg.fiducial.rpa = vox_fids(3,:);
cfg.coordsys = 'neuromag';
mri_realigned = ft_volumerealign(cfg, mri_orig);

% save mri_realigned mri_realigned

% check that the MRI is consistent after realignment
ft_determine_coordsys(mri_realigned, 'interactive', 'no');
hold on; % add the subsequent objects to the figure
drawnow; % workaround to prevent some MATLAB versions (2012b and 2014b) from crashing
ft_plot_headshape(headshape);

%%

cfg = [];
cfg.output = {'brain' 'scalp' 'skull'};
seg = ft_volumesegment(cfg, mri_realigned);

% save seg seg

%%

cfg = [];
cfg.method = 'projectmesh';
cfg.numvertices = 2000;
cfg.tissue = 'brain';
brain = ft_prepare_mesh(cfg, seg);
cfg.tissue = 'skull';
skull = ft_prepare_mesh(cfg, seg);
cfg.tissue = 'scalp';
scalp = ft_prepare_mesh(cfg, seg);

% save brain brain
% save skull skull
% save scalp scalp

%% make the volume conduction model

cfg = [];
cfg.method = 'singleshell';
vol = ft_prepare_headmodel(cfg, brain);

% save vol vol
% save sens sens

ft_determine_coordsys(mri_realigned, 'interactive', 'no')
hold on; % add the subsequent objects to the same figure
ft_plot_headshape(headshape);
ft_plot_headmodel(ft_convert_units(vol, 'mm'));

figure
hold on; % add the subsequent objects to the same figure
ft_plot_headshape(headshape);
ft_plot_sens(ft_convert_units(sens, 'mm'), 'coil', 'yes', 'coilsize', 10);
ft_plot_headmodel(ft_convert_units(vol, 'mm'));

figure
ft_plot_headmodel(ft_convert_units(vol,  'mm'), 'facecolor', 'r'); % FT
ft_plot_headmodel(ft_convert_units(vol1, 'mm'), 'facecolor', 'g'); % SPM
alpha 0.5

Part 2 - reconstruct beta-band power

%% get data from SPM

D = spm_eeg_load('../spm-source-demo/PapMcbdspmeeg_run_01_sss.mat');

disp(D.condlist)

% convert data from SPM
raw      = D.ftraw(D.indchantype('MEGMAG'), D.indsample(-0.1):D.indsample(0.3), D.indtrial(D.condlist{1}, 'GOOD'));
timelock = D.fttimelock(D.indchantype('MEGMAG'), D.indsample(-0.1):D.indsample(0.3), D.indtrial(D.condlist{1}, 'GOOD'));

% alternative method
raw      = spm2fieldtrip(D);
timelock = ft_timelockanalysis([], raw);


%% start from data that was processed by FieldTrip
subj = 15;
prefix = sprintf('Sub%02d', subj);
load([prefix '_raw']);  % this is called "data" rather than "raw"
load([prefix '_avg_Faces_vs_Scrambled']);
load([prefix '_avg_Famous']);
load([prefix '_avg_Unfamiliar']);
load([prefix '_avg_Scrambled']);

% load the results from part 1
load vol
load sens


%% deal with maxfilter

% the data has been maxfiltered and subsequently contatenated
% this results in an ill-conditioned estimate of covariance or CSD

cfg = [];
cfg.method = 'pca';
cfg.updatesens = 'no';
cfg.channel = 'MEGMAG';
comp = ft_componentanalysis(cfg, data);

cfg = [];
cfg.updatesens = 'no';
cfg.component = comp.label(51:end);
data_fix = ft_rejectcomponent(cfg, comp);


%%

cfg = [];
cfg.channel = 'MEGMAG';
cfg.method = 'wavelet';
cfg.output = 'powandcsd';
cfg.foi = 4:2:70;
cfg.toi = -0.200:0.020:1.000;
wavelet = ft_freqanalysis(cfg, data_fix);

% save wavelet wavelet

cfg = [];
cfg.layout = 'neuromag306mag.lay';
cfg.baseline = [-inf 0];
cfg.baselinetype = 'relative';
ft_multiplotTFR(cfg, wavelet)

%%

cfg = [];
cfg.resolution = 7;
% cfg.inwardshift = -7; % allow dipoles 10mm outside the brain, this improves interpolation at the edges
cfg.unit = 'mm';
cfg.headmodel = vol;  % from FT
cfg.grad = sens; % from FT
cfg.senstype = 'meg';
cfg.normalize = 'yes';
grid = ft_prepare_leadfield(cfg, wavelet);

% save grid grid


%% perform whole-brain source reconstruction

cfg = [];
cfg.headmodel = vol;  % from FT
cfg.grad      = sens; % from FT
cfg.senstype  = 'meg';
cfg.grid      = grid;
cfg.method    = 'dics';

cfg.frequency = [14 18];
cfg.latency   = [0.140 0.160];
sourceA = ft_sourceanalysis(cfg, wavelet);
cfg.latency   = [-0.100 -0.080];
sourceB = ft_sourceanalysis(cfg, wavelet);

% cfg.frequency = [40 65];
% cfg.latency   = [0.090 0.140];
% sourceA = ft_sourceanalysis(cfg, wavelet);
% cfg.latency   = [-0.100 -0.050];
% sourceB = ft_sourceanalysis(cfg, wavelet);

%
% cfg.frequency = [12 20];
% cfg.latency   = [0.090 0.140];
% sourceA = ft_sourceanalysis(cfg, wavelet);
% cfg.latency   = [-0.050 0.000];
% sourceB = ft_sourceanalysis(cfg, wavelet);

% FT_MATH requires the time axis needs to be the same
sourceA.time = 0;
sourceB.time = 0;

cfg = [];
cfg.parameter = 'pow';
cfg.operation = 'log10(x1/x2)'; % sourceA divided by sourceB
sourceR = ft_math(cfg, sourceA, sourceB);

cfg = [];
cfg.funparameter = 'pow';
ft_sourceplot(cfg, sourceR);

%% interpolate and plot on individual anatomical MRI

cfg = [];
cfg.parameter = 'pow';
sourceI = ft_sourceinterpolate(cfg, sourceR, mri_realigned);

cfg = [];
cfg.funparameter = 'pow';
ft_sourceplot(cfg, sourceI);

Part 3 - reconstruct single-trial cortical responses

%% start from data that was processed by FieldTrip
subj = 15;
prefix = sprintf('Sub%02d', subj);
load([prefix '_raw']);  % this is called "data" rather than "raw"

%% deal with maxfilter

% the data has been maxfiltered and subsequently concatenated
% this results in an ill-conditioned estimate of covariance or CSD

cfg = [];
cfg.method = 'pca';
cfg.updatesens = 'no';
cfg.channel = 'MEGMAG';
comp = ft_componentanalysis(cfg, data);

cfg = [];
cfg.updatesens = 'no';
cfg.component = comp.label(51:end);
data_fix = ft_rejectcomponent(cfg, comp);

%% compute covariance

cfg = [];
cfg.channel = 'MEGMAG';
cfg.covariance = 'yes'; % compute the covariance for single trials, then average
% cfg.preproc.bpfilter = 'yes';
% cfg.preproc.bpfreq = [5 70];
% cfg.preproc.hpfilter = 'yes';
% cfg.preproc.hpfreq = 1;
% cfg.preproc.derivative = 'yes';
cfg.preproc.demean = 'yes';             % the PCA cleanup shifted the baseline
cfg.preproc.baselinewindow = [-inf 0];  % reapply the baseline correction
cfg.keeptrials = 'yes';
timelock1 = ft_timelockanalysis(cfg, data_fix);

cfg = [];
cfg.covariance = 'yes'; % compute the covariance of the averaged ERF
timelock2 = ft_timelockanalysis(cfg, timelock1);

figure
plot(timelock2.time, timelock2.avg)

cfg = [];
cfg.layout = 'neuromag306mag.lay';
figure; ft_multiplotER(cfg, timelock2);

%%

pos = [21 -64 30];

cfg = [];
cfg.sourcemodel.pos = pos;
cfg.unit = 'mm';
% cfg.grid = grid;
cfg.headmodel = vol;
cfg.grad = sens;
cfg.senstype = 'meg';
cfg.method = 'lcmv';
cfg.lcmv.keepfilter = 'yes';
cfg.lcmv.projectmom = 'yes';
source = ft_sourceanalysis(cfg, timelock2);

figure
plot(source.time, source.avg.mom{1})

%% construct single-trial virtual channel data

virtualchannel_raw = [];
virtualchannel_raw.label = {'source'};
virtualchannel_raw.trialinfo = data_fix.trialinfo;
for i=1:882
% note that this is the non-filtered raw data
virtualchannel_raw.time{i}       = data_fix.time{i};
virtualchannel_raw.trial{i}(1,:) = source.avg.filter{1} * data_fix.trial{i}(:,:);
end

%% average the virtual channel ERP

cfg = [];
cfg.keeptrials = 'yes';
cfg.preproc.demean = 'yes';
cfg.preproc.baselinewindow = [-inf 0];
virtualchannel_avg = ft_timelockanalysis(cfg, virtualchannel_raw);
cfg.trials = virtualchannel_raw.trialinfo==1;
virtualchannel_avg1 = ft_timelockanalysis(cfg, virtualchannel_raw);
cfg.trials = virtualchannel_raw.trialinfo==2;
virtualchannel_avg2 = ft_timelockanalysis(cfg, virtualchannel_raw);
cfg.trials = virtualchannel_raw.trialinfo==3;
virtualchannel_avg3 = ft_timelockanalysis(cfg, virtualchannel_raw);

figure
plot(virtualchannel_avg.time, virtualchannel_avg.avg);

figure
plot(virtualchannel_avg.time, [virtualchannel_avg1.avg; virtualchannel_avg2.avg; virtualchannel_avg3.avg]);
legend({'1-Famous', '2-Unfamiliar', '3-Scrambled'})

figure
imagesc(squeeze(virtualchannel_avg.trial))

%% investigate the virtual channel spectrally

cfg = [];
cfg.method = 'wavelet';
cfg.output = 'pow';
cfg.foi = 4:2:70;
cfg.toi = -0.200:0.020:1.000;
virtualchannel_wavelet = ft_freqanalysis(cfg, virtualchannel_raw);

cfg = [];
cfg.baseline = [-inf 0];
cfg.baselinetype = 'relative';
cfg.interactive = 'no';
ft_singleplotTFR(cfg, virtualchannel_wavelet);