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

[ENH] Enhancements subject / group level GLM and results #443

Merged
merged 7 commits into from
Dec 21, 2021
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
2 changes: 1 addition & 1 deletion miss_hit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ tab_width: 2
# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
metric "cnest": limit 4
metric "file_length": limit 400
metric "cyc": limit 12
metric "cyc": limit 13
metric "parameters": limit 7
35 changes: 35 additions & 0 deletions src/batches/setBatchGroupLevelResults.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
function matlabbatch = setBatchGroupLevelResults(varargin)
%
% Short description of what the function does goes here.
%
% USAGE::
%
% matlabbatch = setBatchGroupLevelResults(matlabbatch, opt, result)
%
% :param matlabbatch:
% :type matlabbatch: structure
% :param opt:
% :type opt: structure
% :param result:
% :type result: structure
%
% :returns: - :matlabbatch: (structure)
%
% (C) Copyright 2019 CPP_SPM developers

[matlabbatch, opt, result] = deal(varargin{:});

result.dir = fullfile(getRFXdir(opt), result.Contrasts.Name);

load(fullfile(result.dir, 'SPM.mat'));
result.nbSubj = SPM.nscan;

result.contrastNb = 1;

result.label = 'group';

result.outputNameStructure = defaultOuputNameStruct(opt, result);

matlabbatch = setBatchResults(matlabbatch, result);

end
19 changes: 14 additions & 5 deletions src/batches/setBatchSubjectLevelGLMSpec.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function matlabbatch = setBatchSubjectLevelGLMSpec(varargin)
%
% Short description of what the function does goes here.
% Sets up the subject level GLM
%
% USAGE::
%
Expand All @@ -15,7 +15,7 @@
% :param subLabel:
% :type subLabel: string
%
% :returns: - :argout1: (structure) (matlabbatch)
% :returns: - :matlabbatch: (structure)
%
% (C) Copyright 2019 CPP_SPM developers

Expand Down Expand Up @@ -55,13 +55,14 @@
% Create ffxDir if it doesnt exist
% If it exists, issue a warning that it has been overwritten
ffxDir = getFFXdir(subLabel, opt);

overwriteDir(ffxDir, opt);

fmri_spec.dir = {ffxDir};

fmri_spec.fact = struct('name', {}, 'levels', {});

fmri_spec.mthresh = getInclusiveMaskThreshold(opt.model.file);

fmri_spec.bases.hrf.derivs = getHRFderivatives(opt.model.file);

fmri_spec.cvi = getSerialCorrelationCorrection(opt.model.file);
Expand Down Expand Up @@ -175,7 +176,15 @@
end
fmriSpec.sess(sesCounter).nscan = numel(hdr);
else
fmriSpec.sess(sesCounter).scans = {fullpathBoldFilename};
if opt.glm.maxNbVols == Inf
scans = {fullpathBoldFilename};
else
scans = cellstr(spm_select('ExtFPList', ...
spm_fileparts(fullpathBoldFilename), ...
spm_file(fullpathBoldFilename, 'filename'), ...
1:opt.glm.maxNbVols));
end
fmriSpec.sess(sesCounter).scans = scans;
end
end

Expand Down Expand Up @@ -216,7 +225,7 @@
mask = getModelMask(opt.model.file);

if isempty(mask) && ...
(strfind(opt.space{1}, 'MNI') || strcmp(opt.space, 'IXI549Space'))
(~isempty(strfind(opt.space{1}, 'MNI')) || strcmp(opt.space, 'IXI549Space'))
mask = spm_select('FPList', fullfile(spm('dir'), 'tpm'), 'mask_ICV.nii');
end

Expand Down
31 changes: 3 additions & 28 deletions src/batches/setBatchSubjectLevelResults.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,14 @@
% :type opt: structure
% :param subLabel:
% :type subLabel: string
% :param iNode:
% :type iNode: positive integer
% :param iCon:
% :type iCon: positive integer
%
% :returns: - :matlabbatch: (structure)
%
% (C) Copyright 2019 CPP_SPM developers

[matlabbatch, opt, subLabel, iNode, iCon] = deal(varargin{:});
[matlabbatch, opt, subLabel, result] = deal(varargin{:});

result.Contrasts = opt.result.Nodes(iNode).Contrasts(iCon);
result.dir = getFFXdir(subLabel, opt);
result.nbSubj = 1;

result.contrastNb = getContrastNb(result, opt);
if isempty(result.contrastNb)
Expand All @@ -33,29 +28,9 @@
return
end

if isfield(opt.result.Nodes(iNode), 'Output')
result.Output = opt.result.Nodes(iNode).Output;
end

result.space = opt.space;

result.label = subLabel;

result.nbSubj = 1;

result.outputNameStructure = struct( ...
'suffix', 'spmT', ...
'ext', '.nii', ...
'use_schema', 'false', ...
'entities', struct('sub', '', ...
'task', strjoin(opt.taskName, ''), ...
'space', result.space, ...
'desc', '', ...
'label', sprintf('%04.0f', ...
result.contrastNb), ...
'p', '', ...
'k', '', ...
'MC', ''));
result.outputNameStructure = defaultOuputNameStruct(opt, result);

matlabbatch = setBatchResults(matlabbatch, result);

Expand Down
39 changes: 39 additions & 0 deletions src/bids_model/getInclusiveMaskThreshold.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
function threshold = getInclusiveMaskThreshold(modelFile, nodeType)
%
% returns the threshold for inclusive masking of subject level GLM
% node of a BIDS statistical model
%
% (C) Copyright 2021 CPP_SPM developers

if isempty(modelFile)
return
end
if nargin < 2 || isempty(nodeType)
nodeType = 'run';
elseif ismember(nodeType, 'dataset')
warning('No inclusive mask threshold for dataset level GLM.');
return
end

model = bids.util.jsondecode(modelFile);

node = returnModelNode(model, nodeType);

try
threshold = node.Model.Software.SPM.InclusiveMaskingThreshold;
catch
threshold = '';
end

if isempty(threshold)

spm_defaults = spm_get_defaults();
threshold = spm_defaults.mask.thresh;

elseif strcmpi(threshold, '-Inf')

threshold = -Inf;

end

end
7 changes: 6 additions & 1 deletion src/defaults/checkOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@
% if you know the order in which slices were acquired, you should be able to recompute
% slice timing and add it to the json files in your BIDS data set.
%
% - ``opt.glm.roibased.do``
% - ``opt.glm.roibased.do = false`` must be set to ``true`` to use the
% ``bidsRoiBasedGLM`` workflow
% - ``opt.glm.maxNbVols = Inf`` sets the maximum number of volumes to
% include in a run in a subject level GLM. This can be useful if some
% time series have more volumes than necessary.
%
% - ``opt.QA.func.carpetPlot = true`` to plot carpet plot when running ``functionaQA``
% - ``opt.QA.func`` contains a lot of options used by ``spmup_first_level_qa``
Expand Down Expand Up @@ -215,6 +219,7 @@
fieldsToSet.QA.func.Basics = 'on';

fieldsToSet.glm.roibased.do = false;
fieldsToSet.glm.maxNbVols = Inf;

% specify the results to compute
fieldsToSet.result.Nodes = returnDefaultResultsStructure();
Expand Down
33 changes: 33 additions & 0 deletions src/results/defaultOuputNameStruct.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function outputNameStructure = defaultOuputNameStruct(opt, result)
%
% Short description of what the function does goes here.
%
% USAGE::
%
% matlabbatch = setBatchSubjectLevelResults(matlabbatch, opt, subLabel, funcFWHM, iNode, iCon)
%
% :param opt:
% :type opt: structure
% :param result:
% :type result: structure
%
% :returns: - :outputNameStructure: (structure)
%
% See also: setBatchSubjectLevelResults, bidsResults
%
% (C) Copyright 2021 CPP_SPM developers

outputNameStructure = struct( ...
'suffix', 'spmT', ...
'ext', '.nii', ...
'use_schema', 'false', ...
'entities', struct('sub', '', ...
'task', strjoin(opt.taskName, ''), ...
'space', result.space, ...
'desc', '', ...
'label', sprintf('%04.0f', result.contrastNb), ...
'p', '', ...
'k', '', ...
'MC', ''));

end
12 changes: 10 additions & 2 deletions src/utils/regexify.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
function string = regexify(string)
%
% Turns a string into a simple regex.
% Turns a string into a simple regex. Useful to query bids dataset with
% bids.query that by default expects will treat its inputs as regex.
%
% Input --> Output
%
% ``foo`` --> ``^foo$``
%
% USAGE::
%
% string = regexify(string)
%
% ``foo`` --> ``^foo$``
%
% (C) Copyright 2021 CPP_SPM developers

Expand Down
4 changes: 3 additions & 1 deletion src/workflows/bidsFFX.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
%
% USAGE::
%
% bidsFFX(action, [opt])
% bidsFFX(action, opt)
%
% :param action: Action to be conducted:``specifyAndEstimate`` or ``contrasts``.
% :type action: string
Expand All @@ -24,6 +24,8 @@
% - ``specifyAndEstimate`` for fMRI design + estimate and
% - ``contrasts`` to estimate contrasts.
%
% See also: setBatchSubjectLevelGLMSpec, setBatchSubjectLevelContrasts
%
% (C) Copyright 2020 CPP_SPM developers

% TODO: get the space to run analysis in from the BIDS stats model input
Expand Down
24 changes: 11 additions & 13 deletions src/workflows/bidsRFX.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@ function bidsRFX(action, opt)
%
% OR
%
% - creates a mean structural image and mean mask over the sample,
% - creates a mean structural image and mean mask over the sample
%
% OR
%
% - specifies and estimates the group level model,
% - computes the group level contrasts.
%
% USAGE::
%
% bidsRFX(action, opt)
%
% :param action: Action to be conducted: ``smoothContrasts`` or ``RFX``
% :param action: Action to be conducted: ``'smoothContrasts'`` or ``'RFX'`` or
% ``'meanAnatAndMask'``
% :type action: string
% :param opt: structure or json filename containing the options. See
% ``checkOptions()`` and ``loadAndCheckOptions()``.
% :type opt: structure
%
% - case ``smoothContrasts``: smooth con images
% - case ``RFX``: Mean Struct, MeanMask, Factorial design specification and
% estimation, Contrast estimation
%
% (C) Copyright 2020 CPP_SPM developers

Expand All @@ -36,9 +37,9 @@ function bidsRFX(action, opt)

matlabbatch = {};

switch action
switch lower(action)

case 'smoothContrasts'
case 'smoothcontrasts'

matlabbatch = setBatchSmoothConImages(matlabbatch, opt);

Expand All @@ -47,7 +48,7 @@ function bidsRFX(action, opt)
'_task-', opt.taskName], ...
opt);

case 'MeanAnatAndMask'
case 'meananatandmask'

opt.dir.rfx = getRFXdir(opt);

Expand All @@ -61,10 +62,7 @@ function bidsRFX(action, opt)
fullfile(opt.dir.stats, 'group'));
saveAndRunWorkflow(matlabbatch, 'create_mean_struc_mask', opt);

case 'RFX'

% TODO
% saving needs to be improved (maybe??) as the name may vary with FWHM and contrast
case 'rfx'

matlabbatch = setBatchFactorialDesign(matlabbatch, opt);

Expand All @@ -90,7 +88,7 @@ function checks(opt, action)
errorHandling(mfilename(), 'tooManySpaces', msg, false, opt.verbosity);
end

allowedActions = {'smoothContrasts', 'MeanAnatAndMask', 'RFX'};
allowedActions = {'smoothContrasts', 'meanAnatAndMask', 'RFX'};
if ~ismember(action, allowedActions)
msg = sprintf('action must be: %s.\n%s was given.', createUnorderedList(allowedActions), ...
action);
Expand Down
Loading