Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions demos/openneuro/ds000114_getOption.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
% returns a structure that contains the options chosen by the user to run
% slice timing correction, pre-processing, FFX, RFX.

if nargin < 1
opt = [];
end

% The directory where the data are located
opt.dataDir = '/home/remi/openneuro/ds000114/raw';

Expand All @@ -18,7 +14,7 @@
opt.taskName = 'linebisection';

opt.anatReference.type = 'T1w';
opt.anatReference.session = 2;
opt.anatReference.session = 'test';

% Uncomment the lines below to run preprocessing
% - don't use realign and unwarp
Expand All @@ -28,11 +24,11 @@

opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
'models', ...
'model-ds000114-linebisection_smdl.json');
'model-ds000114-lineBisectionRunLevel_smdl.json');

% specify the result to compute
opt.result.Steps(1) = struct( ...
'Level', 'subject', ...
'Level', 'run', ...
'Contrasts', struct( ...
'Name', 'Correct_Task', ... % has to match
'Mask', false, ...
Expand Down
23 changes: 23 additions & 0 deletions demos/openneuro/ds000114_run.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,31 @@

bidsSmoothing(FWHM, opt);

%% Run level analysis: as for MVPA

bidsFFX('specifyAndEstimate', opt, FWHM);
bidsFFX('contrasts', opt, FWHM);

bidsConcatBetaTmaps(opt, FWHM, false, false);

%% Subject level analysis: for regular univariate

opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
'models', ...
'model-ds000114-linebisection_smdl.json');

bidsFFX('specifyAndEstimate', opt, FWHM);

opt.result.Steps(1) = struct( ...
'Level', 'subject', ...
'Contrasts', struct( ...
'Name', 'Correct_Task', ... % has to match
'Mask', false, ...
'MC', 'FWE', ... FWE, none, FDR
'p', 0.05, ...
'k', 0));

bidsFFX('contrasts', opt, FWHM);
bidsResults(opt, FWHM);

bidsRFX('smoothContrasts', opt, FWHM, conFWHM);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"Name": "run level",
"Description": "contrasts to compute for the line bisection task",
"Input": {
"task": "linebisection"
},
"Steps": [
{
"Level": "run",
"Model": {
"X": [
"trial_type.Correct_Task",
"trial_type.Incorrect_Task",
"trial_type.No_Response_Task",
"trial_type.Response_Control",
"trial_type.No_Response_Control",
"trans_x",
"trans_y",
"trans_z",
"rot_x",
"rot_y",
"rot_z"
]
},
"AutoContrasts": [
"trial_type.Correct_Task",
"trial_type.Incorrect_Task"
]
}
]
}
29 changes: 24 additions & 5 deletions demos/openneuro/models/model-ds000114-linebisection_smdl.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@
"trial_type.No_Response_Task",
"trial_type.Response_Control",
"trial_type.No_Response_Control",
"trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
"trans_x",
"trans_y",
"trans_z",
"rot_x",
"rot_y",
"rot_z"
]
},
"AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"]
"AutoContrasts": [
"trial_type.Correct_Task",
"trial_type.Incorrect_Task"
]
},
{
"Level": "run",
Expand All @@ -28,14 +36,25 @@
"trial_type.No_Response_Task",
"trial_type.Response_Control",
"trial_type.No_Response_Control",
"trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
"trans_x",
"trans_y",
"trans_z",
"rot_x",
"rot_y",
"rot_z"
]
},
"AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"]
"AutoContrasts": [
"trial_type.Correct_Task",
"trial_type.Incorrect_Task"
]
},
{
"Level": "dataset",
"AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"]
"AutoContrasts": [
"trial_type.Correct_Task",
"trial_type.Incorrect_Task"
]
}
]
}
45 changes: 42 additions & 3 deletions src/workflows/bidsConcatBetaTmaps.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ function bidsConcatBetaTmaps(opt, funcFWHM, deleteIndBeta, deleteIndTmaps)
% :param deleteIndTmaps: decide to delete t-maps
% :type funcFWHM: (boolean)
%
% When concatenating betamaps:
%
% Ensures that there is only 1 image per "contrast".
% Creates a tsv that lists the content of the 4D image.
% This TSV is in the subject level GLM folder where the beta map came from.
% This TSV file is named ``sub-subLabel_task-taskName_space-space_labelfold.tsv``.
%
Copy link
Collaborator

Choose a reason for hiding this comment

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

% in tsv file, folds represent the runs/repetitions of condition, and labels represent trial type (condition).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I was not sure what terms to use because people in different fields use different words to refer to the same thing...

Should we stick with BIDS nomenclature: run and trial_type ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I like BIDS labelling. and it is true that folds are used in some other toolboxes... only thing about the run is, it might be confusing when one has 4D-maps with repetitions (run 1 repetition1, run1 repetition2, ...). No?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ha yes... you are right...

but actually, it could be useful to have both run and repetition, no?

the most "verbose" way of doing this is taking the whole name of the regressor for each image from SPM.mat

this should contain all the info you need: and it is then up to the user to parse this string.

so having the run info AND the regressor_name? there will be a bit of information redundancy because the run number is in the regressor name but...meh...

Copy link
Collaborator

Choose a reason for hiding this comment

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

at the current form, if i am not mistaken, we needed to change the trial type (in _events.tsv file) for GLM to have separate repetitions modelled as separate regressors (betas). so technically it would work like this:

repetition runs trial_type
1 1 Correct_Task*bf(1)_rep1
2 1 Correct_Task*bf(1)_rep2
1 2 Correct_Task*bf(1)_rep1
2 2 Correct_Task*bf(1)_rep2
1 1 Incorrect_Task*bf(1)_rep1
2 1 Incorrect_Task*bf(1)_rep2
1 2 Incorrect_Task*bf(1)_rep1
2 2 Incorrect_Task*bf(1)_rep2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes sort of (I think that SPM always appends the BF at the end)

repetition runs trial_type
1 1 Correct_Task_rep1*bf(1)
2 1 Correct_Task_rep2*bf(1)
1 2 Correct_Task_rep1*bf(1)
2 2 Correct_Task_rep2*bf(1)
...

But the issue that not everyone will have repetitions, but everyone will have runs and "trial_type".

Also the names of the regressors are longer:

'Sn(1) listening*bf(1)'

Where Sn(X) is the number of the 'session' in SPM lingo (more or less a 'run' in the BIDS world... why use the same terms, huh?)


% delete individual Beta and tmaps
if nargin < 3
deleteIndBeta = 1;
deleteIndTmaps = 1;
Expand All @@ -34,24 +40,57 @@ function bidsConcatBetaTmaps(opt, funcFWHM, deleteIndBeta, deleteIndTmaps)

ffxDir = getFFXdir(subLabel, funcFWHM, opt);

load(fullfile(ffxDir, 'SPM.mat'));

contrasts = specifyContrasts(ffxDir, opt.taskName, opt);

beta_maps = cell(length(contrasts), 1);
t_maps = cell(length(contrasts), 1);

% path to beta and t-map files.
for iContrast = 1:length(beta_maps)
% Note that the betas are created from the idx (Beta_idx(iBeta))
fileName = sprintf('beta_%04d.nii', find(contrasts(iContrast).C));

betasIndices = find(contrasts(iContrast).C);

if numel(betasIndices) > 1
error('Supposed to concatenate one beta image per contrast.');
end

% for this beta iamge we identify
% - which run it came from
% - the exact condition name stored in the SPM.mat
% so they can be saved in a tsv for for "label" and "fold" for MVPA
tmp = cat(1, SPM.Sess(:).col) == betasIndices;
runs(iContrast, 1) = find(any(tmp, 2));

tmp = SPM.xX.name{betasIndices};
parts = strsplit(tmp, ' ');
conditions{iContrast, 1} = strjoin(parts(2:end), ' ');

fileName = sprintf('beta_%04d.nii', betasIndices);
fileName = validationInputFile(ffxDir, fileName);
beta_maps{iContrast, 1} = [fileName, ',1'];

% while the contrastes (t-maps) are not from the index. They were created
fileName = sprintf('spmT_%04d.nii', iContrast);
fileName = validationInputFile(ffxDir, fileName);
t_maps{iContrast, 1} = [fileName, ',1'];

end

% tsv
nameStructure = struct( ...
'ext', '.tsv', ...
'type', 'labelfold', ...
'sub', subLabel, ...
'task', opt.taskName, ...
'space', opt.space);
tsvName = createFilename(nameStructure);

tsvContent = struct('folds', runs, 'labels', {conditions});

spm_save(fullfile(ffxDir, tsvName), tsvContent);

% beta maps
outputName = ['4D_beta_', num2str(funcFWHM), '.nii'];

Expand Down