From 8ebbf00d65260985e940471bd45c24044864414d Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 09:54:54 +0100 Subject: [PATCH 01/40] add download instructions for demo dataset with fieldmaps --- demos/openneuro/openneuro.md | 13 +++++++++++++ lib/bids-matlab | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/demos/openneuro/openneuro.md b/demos/openneuro/openneuro.md index fb80298d..9f4c8cfe 100644 --- a/demos/openneuro/openneuro.md +++ b/demos/openneuro/openneuro.md @@ -27,3 +27,16 @@ datalad get sub-0[12]/*/func/*linebisection* datalad get sub-0[12]/anat/*T1w* datalad get sub-0[12]/func/*balloonanalogrisktask* ``` + +## ds001168 + +7T test-retest data set with fieldmaps + +```bash + datalad clone https://github.com/OpenNeuroDatasets/ds001168.git + cd ds001168/ + datalad get sub-0[12]/ses*/anat/* + datalad get sub-0[12]/ses*/func/*fullbrain* + + ``` + diff --git a/lib/bids-matlab b/lib/bids-matlab index dd04a370..cc946cf7 160000 --- a/lib/bids-matlab +++ b/lib/bids-matlab @@ -1 +1 @@ -Subproject commit dd04a3709dcbfd04255cd43ef1caced94872ddc8 +Subproject commit cc946cf7d63c5ad42aa7258ef4ef73a2d24c97e5 From 0e5ac7f23e89cc49c93578c4f64284484fbf5941 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 17:31:57 +0100 Subject: [PATCH 02/40] add basic demo to show how to use fieldmap workflow --- demos/openneuro/ds001168_getOption.m | 27 +++++++++++++++ demos/openneuro/ds001168_run.m | 50 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 demos/openneuro/ds001168_getOption.m create mode 100644 demos/openneuro/ds001168_run.m diff --git a/demos/openneuro/ds001168_getOption.m b/demos/openneuro/ds001168_getOption.m new file mode 100644 index 00000000..bdedc214 --- /dev/null +++ b/demos/openneuro/ds001168_getOption.m @@ -0,0 +1,27 @@ +% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers + +function opt = ds001168_getOption() + % 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/ds001168/raw'; + + % suject to run in each group + opt.subjects = {'01'}; + + % task to analyze + opt.taskName = 'rest'; + + opt.anatReference.type = 'T1w'; + opt.anatReference.session = 1; + + %% DO NOT TOUCH + opt = checkOptions(opt); +% saveOptions(opt); + +end diff --git a/demos/openneuro/ds001168_run.m b/demos/openneuro/ds001168_run.m new file mode 100644 index 00000000..b66b4271 --- /dev/null +++ b/demos/openneuro/ds001168_run.m @@ -0,0 +1,50 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +% runDs001168 + +clear; +clc; +close all; + +% Smoothing to apply +FWHM = 6; + +% directory with this script becomes the current directory +WD = fileparts(mfilename('fullpath')); + +% we add all the subfunctions that are in the sub directories +addpath(fullfile(WD, '..')); +addpath(genpath(fullfile(WD, '..', 'src'))); +addpath(genpath(fullfile(WD, '..', 'lib'))); + +%% Set options +opt = ds001168_getOption(); + +% the line below allows to run preprocessing in "native" space. +% - use realign and unwarp +% - don't do normalization +opt.space = 'individual'; + +checkDependencies(); + +%% Run batches + +% reportBIDS(opt); + +% bidsCopyRawFolder(opt, 1); + +bidsCreateVDM(opt); + +% bidsSTC(opt); +% +% bidsSpatialPrepro(opt); +% +% anatomicalQA(opt); +% bidsResliceTpmToFunc(opt); +% functionalQA(opt); +% +% bidsSmoothing(FWHM, opt); +% +% bidsFFX('specifyAndEstimate', opt, FWHM); +% bidsFFX('contrasts', opt, FWHM); +% bidsResults(opt, FWHM, []); From ca0d663b9e1b7e80428fad386ecb939c2c0d78a4 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 17:32:57 +0100 Subject: [PATCH 03/40] change copy raw to not filter modalities waiting for bids-matlab update to fix that bug --- src/workflows/bidsCopyRawFolder.m | 97 +++++++++++++++++-------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index 7a921091..3572ca15 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -27,8 +27,8 @@ function bidsCopyRawFolder(opt, deleteZippedNii) end opt = loadAndCheckOptions(opt); - % Will only copy those modalities if they exist - modalitiesToCopy = {'anat', 'func'}; + % Will only copy those modalities if they exist +% modalitiesToCopy = {'anat', 'func', 'fmap'}; %% All tasks in this experiment % raw directory and derivatives directory @@ -83,49 +83,56 @@ function bidsCopyRawFolder(opt, deleteZippedNii) mkdir(fullfile(derivativesDir, subDir, sessionFolder)); - modalities = spm_BIDS(BIDS, 'modalities', ... - 'sub', subID, ... - 'ses', sessions{iSes}, ... - 'task', opt.taskName); - - modalities = intersect(modalities, modalitiesToCopy); - - for iModality = 1:numel(modalities) - - try - status = system( ... - sprintf('cp -R -L %s %s', ... - fullfile(rawDir, ... - subDir, ... - sessionFolder, ... - modalities{iModality}), ... - fullfile(derivativesDir, ... - subDir, ... - sessionFolder, ... - modalities{iModality}))); - - if status > 0 - message = [ ... - 'Copying data with system command failed: ' ... - 'Are you running Windows?\n', ... - 'Will use matlab/octave copyfile command instead.\n', ... - 'Maybe your data set contains symbolic links' ... - '(e.g. if you use datalad or git-annex.']; - error(message); - end - - catch - copyfile(fullfile(rawDir, ... - subDir, ... - sessionFolder, ... - modalities{iModality}), ... - fullfile(derivativesDir, ... - subDir, ... - sessionFolder, ... - modalities{iModality})); - end - - end + % TODO + % implement copying specific modalities once bids-matlab can + % query modality for each session + % + % modalities = spm_BIDS(BIDS, 'modalities', ... + % 'sub', subID, ... + % 'ses', sessions{iSes}, ... + % 'task', opt.taskName); + % + % modalities = intersect(modalities, modalitiesToCopy); + + % for iModality = 1:numel(modalities) + % + % mkdir(fullfile(derivativesDir, subDir, sessionFolder, modalities{iModality})) + + % srcFolder = fullfile(rawDir, ... + % subDir, ... + % sessionFolder, ... + % modalities{iModality}); + % targetFolder = fullfile(derivativesDir, ... + % subDir, ... + % sessionFolder, ... + % modalities{iModality}); + + srcFolder = fullfile(rawDir, ... + subDir, ... + sessionFolder); + targetFolder = fullfile(derivativesDir, ... + subDir); + + try + status = system( ... + sprintf('cp -R -L -f %s %s', ... + srcFolder, ... + targetFolder)); + + if status > 0 + message = [ ... + 'Copying data with system command failed: ' ... + 'Are you running Windows?\n', ... + 'Will use matlab/octave copyfile command instead.\n', ... + 'Maybe your data set contains symbolic links' ... + '(e.g. if you use datalad or git-annex.']; + error(message); + end + + catch + fprintf(1, 'Using octave/matlab to copy files.'); + copyfile(srcFolder, targetFolder); + end end From 16bf11f0ce588add52172aa922ded0f61230dc7e Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 17:33:24 +0100 Subject: [PATCH 04/40] create rough draft of workflow --- src/workflows/bidsCreateVDM.m | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/workflows/bidsCreateVDM.m diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m new file mode 100644 index 00000000..6d7735a8 --- /dev/null +++ b/src/workflows/bidsCreateVDM.m @@ -0,0 +1,49 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function bidsCreateVDM(opt) + % bidsCreateVDM(opt) + % + % adapted from spmup spmup_BIDS_preprocess (@ commit + % 198c980d6d7520b1a996f0e56269e2ceab72cc83) + + if nargin < 1 + opt = []; + end + opt = loadAndCheckOptions(opt); + + % load the subjects/Groups information and the task name + [group, opt, BIDS] = getData(opt); + + fprintf(1, ' FIELDMAP WORKFLOW\n'); + + %% Loop through the groups, subjects, and sessions + for iGroup = 1:length(group) + + groupName = group(iGroup).name; + + for iSub = 1:group(iGroup).numSub + + subID = group(iGroup).subNumber{iSub}; + + types = spm_BIDS(BIDS, 'types', 'sub', subID); + + if any(ismember(types, {'phase12', 'phasediff', 'fieldmap', 'epi'})) + + printProcessingSubject(groupName, iSub, subID); + + fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); + + matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); + + % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); + + % spm_jobman('run', matlabbatch); + + % fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); + + end + + end + + end +end From 66b640ebcc085761f4ae98435a1d7ab7eb998700 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 17:34:26 +0100 Subject: [PATCH 05/40] add batches for coreg --- src/batches/setBatchCoregistrationFmap.m | 28 +++++++++++++++++++++ src/batches/setBatchCoregistrationGeneral.m | 14 +++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/batches/setBatchCoregistrationFmap.m create mode 100644 src/batches/setBatchCoregistrationGeneral.m diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m new file mode 100644 index 00000000..21f94fa9 --- /dev/null +++ b/src/batches/setBatchCoregistrationFmap.m @@ -0,0 +1,28 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID) + + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + + % Create rough mean of the 1rst run to improve SNR for coregistration + % TODO use the slice timed EPI if STC was used + runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1}); + [fileName, subFuncDataDir] = getBoldFilename(BIDS, subID, sessions{1}, runs{1}, opt); + + spmup_basics(fullfile(subFuncDataDir, fileName), 'mean'); + + refImage = fullfile(subFuncDataDir, ['mean_', fileName]); + + matlabbatch = []; + + + + + +% for iSes = 1:nbSessions +% +% matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, src, other); +% +% end + +end diff --git a/src/batches/setBatchCoregistrationGeneral.m b/src/batches/setBatchCoregistrationGeneral.m new file mode 100644 index 00000000..0c41117e --- /dev/null +++ b/src/batches/setBatchCoregistrationGeneral.m @@ -0,0 +1,14 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) + % matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) + % + % ref: string + % src: string + % other: cell string + + matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref = { ref }; + matlabbatch{end}.spm.spatial.coreg.estimate.source = { src }; + matlabbatch{end}.spm.spatial.coreg.estimate.other = other; + +end From 94a3ffb50a397952f8a4b4b8fdd320ac0f6f6ff9 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 17:35:00 +0100 Subject: [PATCH 06/40] small change to getInfo --- src/getInfo.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/getInfo.m b/src/getInfo.m index 98ca7b69..7277144e 100644 --- a/src/getInfo.m +++ b/src/getInfo.m @@ -79,9 +79,16 @@ varargout = fileName; - otherwise - error('Not sure what info you want me to get.'); + varargout = spm_BIDS(BIDS, 'data', ... + 'sub', subID, ... + 'run', run, ... + 'ses', session, ... + 'task', opt.taskName, ... + 'type', type); - end + otherwise + error('Not sure what info you want me to get.'); + + end end From e5cf22b6cd75df785da805023dc776fb594a73c0 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sun, 25 Oct 2020 22:05:05 +0100 Subject: [PATCH 07/40] finish first draft of coregistration of fmap --- src/batches/setBatchCoregistrationFmap.m | 32 ++++++++++++++++-------- src/workflows/bidsCreateVDM.m | 5 ++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m index 21f94fa9..ba7cc35e 100644 --- a/src/batches/setBatchCoregistrationFmap.m +++ b/src/batches/setBatchCoregistrationFmap.m @@ -1,28 +1,38 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID) - - [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); - + + % TODO + % assumes all the fieldmap relate to the current task + % use the "for" metadata field + % Create rough mean of the 1rst run to improve SNR for coregistration % TODO use the slice timed EPI if STC was used + sessions = getInfo(BIDS, subID, opt, 'Sessions'); runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1}); [fileName, subFuncDataDir] = getBoldFilename(BIDS, subID, sessions{1}, runs{1}, opt); spmup_basics(fullfile(subFuncDataDir, fileName), 'mean'); refImage = fullfile(subFuncDataDir, ['mean_', fileName]); - - matlabbatch = []; + matlabbatch = []; + % TODO Move to getInfo + fmapFiles = spm_BIDS(BIDS, 'data', 'modality', 'fmap'); + for iRun = 1:size(fmapFiles, 1) + + % TODO only deals with phasdiff for the moment + srcImage = strrep(fmapFiles{iRun}, 'phasediff', 'magnitude1'); + + otherImages = cell(2,1); + otherImages{1} = strrep(fmapFiles{iRun}, 'phasediff', 'magnitude2'); + otherImages{2} = fmapFiles{iRun}; + + matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, srcImage, otherImages); + + end -% for iSes = 1:nbSessions -% -% matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, src, other); -% -% end - end diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index 6d7735a8..120541d1 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -25,6 +25,7 @@ function bidsCreateVDM(opt) subID = group(iGroup).subNumber{iSub}; + % TODO Move to getInfo types = spm_BIDS(BIDS, 'types', 'sub', subID); if any(ismember(types, {'phase12', 'phasediff', 'fieldmap', 'epi'})) @@ -35,9 +36,9 @@ function bidsCreateVDM(opt) matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); - % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); + saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); - % spm_jobman('run', matlabbatch); + spm_jobman('run', matlabbatch); % fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); From f3af70c51dd9b139c29177608f1a61f2ec65c8cf Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Mon, 26 Oct 2020 00:29:34 +0100 Subject: [PATCH 08/40] minor changes --- src/getInfo.m | 10 +++++----- src/utils/loadAndCheckOptions.m | 13 +++++++------ src/{ => utils}/saveMatlabBatch.m | 0 src/workflows/bidsCopyRawFolder.m | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) rename src/{ => utils}/saveMatlabBatch.m (100%) diff --git a/src/getInfo.m b/src/getInfo.m index 7277144e..f1fe92d3 100644 --- a/src/getInfo.m +++ b/src/getInfo.m @@ -80,11 +80,11 @@ varargout = fileName; varargout = spm_BIDS(BIDS, 'data', ... - 'sub', subID, ... - 'run', run, ... - 'ses', session, ... - 'task', opt.taskName, ... - 'type', type); + 'sub', subID, ... + 'run', run, ... + 'ses', session, ... + 'task', opt.taskName, ... + 'type', type); otherwise error('Not sure what info you want me to get.'); diff --git a/src/utils/loadAndCheckOptions.m b/src/utils/loadAndCheckOptions.m index 3c3b79b6..86f963f8 100644 --- a/src/utils/loadAndCheckOptions.m +++ b/src/utils/loadAndCheckOptions.m @@ -19,12 +19,13 @@ return end - if size(opt, 1) > 1 - containsDate = cellfun(@any, strfind(cellstr(opt), '_date-')); - opt = opt(containsDate, :); - opt = sortrows(opt); - opt = opt(end, :); - end + % finds most recent option file + if size(opt, 1) > 1 + containsDate = cellfun(@any, strfind(cellstr(opt), '_date-')); + opt = opt(containsDate, :); + opt = sortrows(opt); + opt = opt(end, :); + end if ischar(opt) && exist(opt, 'file') fprintf(1, '\nReading option from: %s.\n', opt); diff --git a/src/saveMatlabBatch.m b/src/utils/saveMatlabBatch.m similarity index 100% rename from src/saveMatlabBatch.m rename to src/utils/saveMatlabBatch.m diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index 3572ca15..9c06d9ea 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -28,7 +28,7 @@ function bidsCopyRawFolder(opt, deleteZippedNii) opt = loadAndCheckOptions(opt); % Will only copy those modalities if they exist -% modalitiesToCopy = {'anat', 'func', 'fmap'}; + % modalitiesToCopy = {'anat', 'func', 'fmap'}; %% All tasks in this experiment % raw directory and derivatives directory From 9498b1623fc3d4a3c4b398b0090a7327bedc6d13 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Mon, 26 Oct 2020 00:32:26 +0100 Subject: [PATCH 09/40] update fmap workflow --- src/batches/setBatchComputeVDM.m | 65 ++++++++++++++++++ src/batches/setBatchCoregistrationFmap.m | 63 +++++++++++------- src/batches/setBatchCreateVDMs.m | 84 ++++++++++++++++++++++++ src/workflows/bidsCreateVDM.m | 53 ++++++++------- 4 files changed, 218 insertions(+), 47 deletions(-) create mode 100644 src/batches/setBatchComputeVDM.m create mode 100644 src/batches/setBatchCreateVDMs.m diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m new file mode 100644 index 00000000..e6d1f4d4 --- /dev/null +++ b/src/batches/setBatchComputeVDM.m @@ -0,0 +1,65 @@ +% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchComputeVDM(matlabbatch, fmapType) + % matlabbatch = setBatchComputeVDM(type) + % + % adapted from spmup get_FM_workflow.m (@ commit + % 198c980d6d7520b1a996f0e56269e2ceab72cc83) + + switch lower(fmapType) + case 'phasediff' + matlabbatch{end + 1}.spm.tools.fieldmap.calculatevdm.subj(1).data.presubphasemag.phase = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.presubphasemag.magnitude = ''; + + case 'phase&mag' + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.shortphase = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.shortmag = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.longphase = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.longmag = ''; + + otherwise + error('This type of field map is not handled: %s', fmapType); + end + + FM_template = fullfile( ... + spm('dir'), ... + 'toolbox', ... + 'FieldMap', ... + 'T1.nii'); + + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.mflags.template = {FM_template}; + + UF = struct( ... + 'method', 'Mark3D', ... + 'fwhm', 10, ... + 'pad', 0, ... + 'ws', 1); + + MF = struct( ... + 'template', '', ... + 'fwhm', 5, ... + 'nerode', 2, ... + 'ndilate', 4, ... + 'thresh', 0.5, ... + 'reg', 0.02); + + defaultsval = struct( ... + 'et', [NaN NaN], ... + 'maskbrain', 1, ... + 'blipdir', 1, ... % can be changed + 'tert', '', ... + 'epifm', 0, ... % can be changed + 'ajm', 0, ... + 'uflags', UF, ... + 'mflags', MF); + + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).defaults.defaultsval = defaultsval; + + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).session.epi = ''; % value needed + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchvdm = 1; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).sessname = 'run'; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).writeunwarped = 1; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).anat = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchanat = 0; + +end diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m index ba7cc35e..47dc9793 100644 --- a/src/batches/setBatchCoregistrationFmap.m +++ b/src/batches/setBatchCoregistrationFmap.m @@ -1,38 +1,51 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID) - + % TODO % assumes all the fieldmap relate to the current task - % use the "for" metadata field - + % - use the "for" metadata field + % - implement for 'phase12', 'fieldmap', 'epi' + % Create rough mean of the 1rst run to improve SNR for coregistration - % TODO use the slice timed EPI if STC was used - sessions = getInfo(BIDS, subID, opt, 'Sessions'); + % TODO use the slice timed EPI if STC was used ? + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1}); [fileName, subFuncDataDir] = getBoldFilename(BIDS, subID, sessions{1}, runs{1}, opt); - + spmup_basics(fullfile(subFuncDataDir, fileName), 'mean'); - + refImage = fullfile(subFuncDataDir, ['mean_', fileName]); - + matlabbatch = []; - - % TODO Move to getInfo - fmapFiles = spm_BIDS(BIDS, 'data', 'modality', 'fmap'); - - for iRun = 1:size(fmapFiles, 1) - - % TODO only deals with phasdiff for the moment - srcImage = strrep(fmapFiles{iRun}, 'phasediff', 'magnitude1'); - - otherImages = cell(2,1); - otherImages{1} = strrep(fmapFiles{iRun}, 'phasediff', 'magnitude2'); - otherImages{2} = fmapFiles{iRun}; - - matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, srcImage, otherImages); - + + for iSes = 1:nbSessions + + runs = spm_BIDS(BIDS, 'runs', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}); + + for iRun = 1:numel(runs) + + % TODO + % - Move to getInfo + fmapFiles = spm_BIDS(BIDS, 'data', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'run', runs{iRun}); + + srcImage = strrep(fmapFiles{1}, 'phasediff', 'magnitude1'); + + otherImages = cell(2, 1); + otherImages{1} = strrep(fmapFiles{1}, 'phasediff', 'magnitude2'); + otherImages{2} = fmapFiles{1}; + + matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, srcImage, otherImages); + + end + end - - + end diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m new file mode 100644 index 00000000..a96ba45d --- /dev/null +++ b/src/batches/setBatchCreateVDMs.m @@ -0,0 +1,84 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchCreateVDMs(opt, BIDS, subID) + + % TODO + % assumes all the fieldmap relate to the current task + % - use the "for" metadata field + % - implement for 'phase12', 'fieldmap', 'epi' + + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + + matlabbatch = []; + for iSes = 1:nbSessions + + runs = spm_BIDS(BIDS, 'runs', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}); + + for iRun = 1:numel(runs) + + matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff'); + + % TODO + % - Move to getInfo + fmapFiles = spm_BIDS(BIDS, 'data', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'run', runs{iRun}); + + phaseImage = fmapFiles{1}; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.phase = ... + {phaseImage}; + + magnitudeImage = strrep(phaseImage, 'phasediff', 'magnitude1'); + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.magnitude = ... + {magnitudeImage}; + + metadata = spm_BIDS(BIDS, 'metadata', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'run', runs{iRun}); + + echotimes = 1000 * [ ... + metadata.EchoTime1, ... + metadata.EchoTime2]; % in milliseconds + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; + + totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID); + + end + + end + +end + +function totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID) + + metadata = spm_BIDS(BIDS, 'metadata', ... + 'type', 'bold', ... + 'sub', subID, ... + 'task', opt.taskName); + + % func run metadata: if the fmap is applied to several + % runs we take the metadata of the first run it must + % applied to + if numel(metadata) > 1 + metadata = metadata{1}; + end + + if isfield(metadata, 'TotalReadoutTime') + totalReadoutTime = metadata.TotalReadoutTime; + + elseif isfield(metadata, 'RepetitionTime') + totalReadoutTime = metadata.RepetitionTime; + + elseif isfield(metadata, 'EffectiveEchoSpacing') + totalReadoutTime = (metadata.NumberOfEchos - 1) ... + * metadata.EffectiveEchoSpacing; + end + +end diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index 120541d1..93015a2d 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -3,48 +3,57 @@ function bidsCreateVDM(opt) % bidsCreateVDM(opt) % - % adapted from spmup spmup_BIDS_preprocess (@ commit + % inspired from spmup spmup_BIDS_preprocess (@ commit % 198c980d6d7520b1a996f0e56269e2ceab72cc83) - + if nargin < 1 opt = []; end opt = loadAndCheckOptions(opt); - + % load the subjects/Groups information and the task name [group, opt, BIDS] = getData(opt); - + fprintf(1, ' FIELDMAP WORKFLOW\n'); - + %% Loop through the groups, subjects, and sessions for iGroup = 1:length(group) - + groupName = group(iGroup).name; - + for iSub = 1:group(iGroup).numSub - + subID = group(iGroup).subNumber{iSub}; - + % TODO Move to getInfo types = spm_BIDS(BIDS, 'types', 'sub', subID); - + if any(ismember(types, {'phase12', 'phasediff', 'fieldmap', 'epi'})) - + printProcessingSubject(groupName, iSub, subID); - + fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); - + matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); - - saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); - - spm_jobman('run', matlabbatch); - - % fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); - + % + % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); + + % spm_jobman('run', matlabbatch); + + fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); + + matlabbatch = setBatchCreateVDMs(opt, BIDS, subID); + + saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID); + + % spm_jobman('run', matlabbatch); + + % TODO + % delete temporary mean images ?? + end - + end - + end end From ade8561c9be64e1b4fc8c474d329bfc696ad97d5 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 12:48:01 +0100 Subject: [PATCH 10/40] change tab width --- demos/openneuro/ds000001_getOption.m | 2 +- demos/openneuro/ds000001_run.m | 2 +- demos/openneuro/ds000114_getOption.m | 2 +- demos/openneuro/ds000114_run.m | 2 +- demos/openneuro/ds001168_getOption.m | 32 +++--- src/batches/setBatchComputeVDM.m | 100 ++++++++--------- src/batches/setBatchCoregistrationFmap.m | 64 +++++------ src/batches/setBatchCoregistrationGeneral.m | 16 +-- src/batches/setBatchCreateVDMs.m | 112 ++++++++++---------- src/getInfo.m | 27 ++--- src/utils/checkOptions.m | 106 ++++++++++++++++++ src/utils/loadAndCheckOptions.m | 14 +-- src/workflows/bidsCopyRawFolder.m | 104 +++++++++--------- src/workflows/bidsCreateVDM.m | 68 ++++++------ 14 files changed, 374 insertions(+), 277 deletions(-) create mode 100644 src/utils/checkOptions.m diff --git a/demos/openneuro/ds000001_getOption.m b/demos/openneuro/ds000001_getOption.m index 07be6d40..6de06884 100644 --- a/demos/openneuro/ds000001_getOption.m +++ b/demos/openneuro/ds000001_getOption.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function opt = ds000001_getOption() % returns a structure that contains the options chosen by the user to run diff --git a/demos/openneuro/ds000001_run.m b/demos/openneuro/ds000001_run.m index c9201d39..a8b7f066 100644 --- a/demos/openneuro/ds000001_run.m +++ b/demos/openneuro/ds000001_run.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers % runDs00014 diff --git a/demos/openneuro/ds000114_getOption.m b/demos/openneuro/ds000114_getOption.m index 626aa34a..816f0561 100644 --- a/demos/openneuro/ds000114_getOption.m +++ b/demos/openneuro/ds000114_getOption.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function opt = ds000114_getOption() % returns a structure that contains the options chosen by the user to run diff --git a/demos/openneuro/ds000114_run.m b/demos/openneuro/ds000114_run.m index dc4b0424..1ddd6efc 100644 --- a/demos/openneuro/ds000114_run.m +++ b/demos/openneuro/ds000114_run.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers % runDs00014 diff --git a/demos/openneuro/ds001168_getOption.m b/demos/openneuro/ds001168_getOption.m index bdedc214..c85d66f3 100644 --- a/demos/openneuro/ds001168_getOption.m +++ b/demos/openneuro/ds001168_getOption.m @@ -1,27 +1,27 @@ % (C) Copyright 2019 CPP BIDS SPM-pipeline developpers function opt = ds001168_getOption() - % returns a structure that contains the options chosen by the user to run - % slice timing correction, pre-processing, FFX, RFX. + % 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 + if nargin < 1 + opt = []; + end - % The directory where the data are located - opt.dataDir = '/home/remi/openneuro/ds001168/raw'; + % The directory where the data are located + opt.dataDir = '/home/remi/openneuro/ds001168/raw'; - % suject to run in each group - opt.subjects = {'01'}; + % suject to run in each group + opt.subjects = {'01'}; - % task to analyze - opt.taskName = 'rest'; + % task to analyze + opt.taskName = 'rest'; - opt.anatReference.type = 'T1w'; - opt.anatReference.session = 1; + opt.anatReference.type = 'T1w'; + opt.anatReference.session = 1; - %% DO NOT TOUCH - opt = checkOptions(opt); -% saveOptions(opt); + %% DO NOT TOUCH + opt = checkOptions(opt); + % saveOptions(opt); end diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m index e6d1f4d4..903e9d2d 100644 --- a/src/batches/setBatchComputeVDM.m +++ b/src/batches/setBatchComputeVDM.m @@ -1,65 +1,65 @@ % (C) Copyright 2019 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchComputeVDM(matlabbatch, fmapType) - % matlabbatch = setBatchComputeVDM(type) - % - % adapted from spmup get_FM_workflow.m (@ commit - % 198c980d6d7520b1a996f0e56269e2ceab72cc83) + % matlabbatch = setBatchComputeVDM(type) + % + % adapted from spmup get_FM_workflow.m (@ commit + % 198c980d6d7520b1a996f0e56269e2ceab72cc83) - switch lower(fmapType) - case 'phasediff' - matlabbatch{end + 1}.spm.tools.fieldmap.calculatevdm.subj(1).data.presubphasemag.phase = ''; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.presubphasemag.magnitude = ''; + switch lower(fmapType) + case 'phasediff' + matlabbatch{end + 1}.spm.tools.fieldmap.calculatevdm.subj(1).data.presubphasemag.phase = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.presubphasemag.magnitude = ''; - case 'phase&mag' - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.shortphase = ''; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.shortmag = ''; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.longphase = ''; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.longmag = ''; + case 'phase&mag' + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.shortphase = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.shortmag = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.longphase = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).data.phasemag.longmag = ''; - otherwise - error('This type of field map is not handled: %s', fmapType); - end + otherwise + error('This type of field map is not handled: %s', fmapType); + end - FM_template = fullfile( ... - spm('dir'), ... - 'toolbox', ... - 'FieldMap', ... - 'T1.nii'); + FM_template = fullfile( ... + spm('dir'), ... + 'toolbox', ... + 'FieldMap', ... + 'T1.nii'); - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.mflags.template = {FM_template}; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.mflags.template = {FM_template}; - UF = struct( ... - 'method', 'Mark3D', ... - 'fwhm', 10, ... - 'pad', 0, ... - 'ws', 1); + UF = struct( ... + 'method', 'Mark3D', ... + 'fwhm', 10, ... + 'pad', 0, ... + 'ws', 1); - MF = struct( ... - 'template', '', ... - 'fwhm', 5, ... - 'nerode', 2, ... - 'ndilate', 4, ... - 'thresh', 0.5, ... - 'reg', 0.02); + MF = struct( ... + 'template', '', ... + 'fwhm', 5, ... + 'nerode', 2, ... + 'ndilate', 4, ... + 'thresh', 0.5, ... + 'reg', 0.02); - defaultsval = struct( ... - 'et', [NaN NaN], ... - 'maskbrain', 1, ... - 'blipdir', 1, ... % can be changed - 'tert', '', ... - 'epifm', 0, ... % can be changed - 'ajm', 0, ... - 'uflags', UF, ... - 'mflags', MF); + defaultsval = struct( ... + 'et', [NaN NaN], ... + 'maskbrain', 1, ... + 'blipdir', 1, ... % can be changed + 'tert', '', ... + 'epifm', 0, ... % can be changed + 'ajm', 0, ... + 'uflags', UF, ... + 'mflags', MF); - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).defaults.defaultsval = defaultsval; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).defaults.defaultsval = defaultsval; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).session.epi = ''; % value needed - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchvdm = 1; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).sessname = 'run'; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).writeunwarped = 1; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).anat = ''; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchanat = 0; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).session.epi = ''; % value needed + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchvdm = 1; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).sessname = 'run'; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).writeunwarped = 1; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).anat = ''; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchanat = 0; end diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m index 47dc9793..9e11206f 100644 --- a/src/batches/setBatchCoregistrationFmap.m +++ b/src/batches/setBatchCoregistrationFmap.m @@ -2,50 +2,50 @@ function matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID) - % TODO - % assumes all the fieldmap relate to the current task - % - use the "for" metadata field - % - implement for 'phase12', 'fieldmap', 'epi' + % TODO + % assumes all the fieldmap relate to the current task + % - use the "for" metadata field + % - implement for 'phase12', 'fieldmap', 'epi' - % Create rough mean of the 1rst run to improve SNR for coregistration - % TODO use the slice timed EPI if STC was used ? - [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); - runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1}); - [fileName, subFuncDataDir] = getBoldFilename(BIDS, subID, sessions{1}, runs{1}, opt); + % Create rough mean of the 1rst run to improve SNR for coregistration + % TODO use the slice timed EPI if STC was used ? + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1}); + [fileName, subFuncDataDir] = getBoldFilename(BIDS, subID, sessions{1}, runs{1}, opt); - spmup_basics(fullfile(subFuncDataDir, fileName), 'mean'); + spmup_basics(fullfile(subFuncDataDir, fileName), 'mean'); - refImage = fullfile(subFuncDataDir, ['mean_', fileName]); + refImage = fullfile(subFuncDataDir, ['mean_', fileName]); - matlabbatch = []; + matlabbatch = []; - for iSes = 1:nbSessions + for iSes = 1:nbSessions - runs = spm_BIDS(BIDS, 'runs', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessions{iSes}); + runs = spm_BIDS(BIDS, 'runs', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}); - for iRun = 1:numel(runs) + for iRun = 1:numel(runs) - % TODO - % - Move to getInfo - fmapFiles = spm_BIDS(BIDS, 'data', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessions{iSes}, ... - 'run', runs{iRun}); + % TODO + % - Move to getInfo + fmapFiles = spm_BIDS(BIDS, 'data', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'run', runs{iRun}); - srcImage = strrep(fmapFiles{1}, 'phasediff', 'magnitude1'); + srcImage = strrep(fmapFiles{1}, 'phasediff', 'magnitude1'); - otherImages = cell(2, 1); - otherImages{1} = strrep(fmapFiles{1}, 'phasediff', 'magnitude2'); - otherImages{2} = fmapFiles{1}; + otherImages = cell(2, 1); + otherImages{1} = strrep(fmapFiles{1}, 'phasediff', 'magnitude2'); + otherImages{2} = fmapFiles{1}; - matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, srcImage, otherImages); - - end + matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, srcImage, otherImages); end + end + end diff --git a/src/batches/setBatchCoregistrationGeneral.m b/src/batches/setBatchCoregistrationGeneral.m index 0c41117e..72c9fc24 100644 --- a/src/batches/setBatchCoregistrationGeneral.m +++ b/src/batches/setBatchCoregistrationGeneral.m @@ -1,14 +1,14 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) - % matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) - % - % ref: string - % src: string - % other: cell string + % matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) + % + % ref: string + % src: string + % other: cell string - matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref = { ref }; - matlabbatch{end}.spm.spatial.coreg.estimate.source = { src }; - matlabbatch{end}.spm.spatial.coreg.estimate.other = other; + matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref = { ref }; + matlabbatch{end}.spm.spatial.coreg.estimate.source = { src }; + matlabbatch{end}.spm.spatial.coreg.estimate.other = other; end diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m index a96ba45d..a0ad651a 100644 --- a/src/batches/setBatchCreateVDMs.m +++ b/src/batches/setBatchCreateVDMs.m @@ -2,83 +2,83 @@ function matlabbatch = setBatchCreateVDMs(opt, BIDS, subID) - % TODO - % assumes all the fieldmap relate to the current task - % - use the "for" metadata field - % - implement for 'phase12', 'fieldmap', 'epi' + % TODO + % assumes all the fieldmap relate to the current task + % - use the "for" metadata field + % - implement for 'phase12', 'fieldmap', 'epi' - [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); - matlabbatch = []; - for iSes = 1:nbSessions + matlabbatch = []; + for iSes = 1:nbSessions - runs = spm_BIDS(BIDS, 'runs', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessions{iSes}); + runs = spm_BIDS(BIDS, 'runs', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}); - for iRun = 1:numel(runs) + for iRun = 1:numel(runs) - matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff'); + matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff'); - % TODO - % - Move to getInfo - fmapFiles = spm_BIDS(BIDS, 'data', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessions{iSes}, ... - 'run', runs{iRun}); + % TODO + % - Move to getInfo + fmapFiles = spm_BIDS(BIDS, 'data', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'run', runs{iRun}); - phaseImage = fmapFiles{1}; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.phase = ... - {phaseImage}; + phaseImage = fmapFiles{1}; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.phase = ... + {phaseImage}; - magnitudeImage = strrep(phaseImage, 'phasediff', 'magnitude1'); - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.magnitude = ... - {magnitudeImage}; + magnitudeImage = strrep(phaseImage, 'phasediff', 'magnitude1'); + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.magnitude = ... + {magnitudeImage}; - metadata = spm_BIDS(BIDS, 'metadata', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessions{iSes}, ... - 'run', runs{iRun}); + metadata = spm_BIDS(BIDS, 'metadata', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'run', runs{iRun}); - echotimes = 1000 * [ ... - metadata.EchoTime1, ... - metadata.EchoTime2]; % in milliseconds - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; + echotimes = 1000 * [ ... + metadata.EchoTime1, ... + metadata.EchoTime2]; % in milliseconds + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; - totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID); - - end + totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID); end + end + end function totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID) - metadata = spm_BIDS(BIDS, 'metadata', ... - 'type', 'bold', ... - 'sub', subID, ... - 'task', opt.taskName); + metadata = spm_BIDS(BIDS, 'metadata', ... + 'type', 'bold', ... + 'sub', subID, ... + 'task', opt.taskName); - % func run metadata: if the fmap is applied to several - % runs we take the metadata of the first run it must - % applied to - if numel(metadata) > 1 - metadata = metadata{1}; - end + % func run metadata: if the fmap is applied to several + % runs we take the metadata of the first run it must + % applied to + if numel(metadata) > 1 + metadata = metadata{1}; + end - if isfield(metadata, 'TotalReadoutTime') - totalReadoutTime = metadata.TotalReadoutTime; + if isfield(metadata, 'TotalReadoutTime') + totalReadoutTime = metadata.TotalReadoutTime; - elseif isfield(metadata, 'RepetitionTime') - totalReadoutTime = metadata.RepetitionTime; + elseif isfield(metadata, 'RepetitionTime') + totalReadoutTime = metadata.RepetitionTime; - elseif isfield(metadata, 'EffectiveEchoSpacing') - totalReadoutTime = (metadata.NumberOfEchos - 1) ... - * metadata.EffectiveEchoSpacing; - end + elseif isfield(metadata, 'EffectiveEchoSpacing') + totalReadoutTime = (metadata.NumberOfEchos - 1) ... + * metadata.EffectiveEchoSpacing; + end end diff --git a/src/getInfo.m b/src/getInfo.m index f1fe92d3..877f6efd 100644 --- a/src/getInfo.m +++ b/src/getInfo.m @@ -70,25 +70,16 @@ [session, run, type] = deal(varargin{:}); - fileName = spm_BIDS(BIDS, 'data', ... - 'sub', subID, ... - 'run', run, ... - 'ses', session, ... - 'task', opt.taskName, ... - 'type', type); - - varargout = fileName; - - varargout = spm_BIDS(BIDS, 'data', ... - 'sub', subID, ... - 'run', run, ... - 'ses', session, ... - 'task', opt.taskName, ... - 'type', type); + varargout = spm_BIDS(BIDS, 'data', ... + 'sub', subID, ... + 'run', run, ... + 'ses', session, ... + 'task', opt.taskName, ... + 'type', type); - otherwise - error('Not sure what info you want me to get.'); + otherwise + error('Not sure what info you want me to get.'); - end + end end diff --git a/src/utils/checkOptions.m b/src/utils/checkOptions.m new file mode 100644 index 00000000..2b4eb57a --- /dev/null +++ b/src/utils/checkOptions.m @@ -0,0 +1,106 @@ +% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers + +function opt = checkOptions(opt) + % opt = checkOptions(opt) + % + % we check the option inputs and add any missing field with some defaults + + fieldsToSet = setDefaultOption(); + + opt = setDefaultFields(opt, fieldsToSet); + + if ~isfield(opt, 'taskName') || isempty(opt.taskName) + + errorStruct.identifier = 'checkOptions:noTask'; + errorStruct.message = sprintf( ... + 'Provide the name of the task to analyze.'); + error(errorStruct); + + end + + if ~all(cellfun(@ischar, opt.groups)) + + disp(opt.groups); + + errorStruct.identifier = 'checkOptions:groupNotString'; + errorStruct.message = sprintf( ... + 'All group names should be string.'); + error(errorStruct); + + end + + if ~isempty (opt.STC_referenceSlice) && length(opt.STC_referenceSlice) > 1 + + errorStruct.identifier = 'checkOptions:refSliceNotScalar'; + errorStruct.message = sprintf( ... + ['options.STC_referenceSlice should be a scalar.' ... + '\nCurrent value is: %d'], ... + opt.STC_referenceSlice); + error(errorStruct); + + end + + if ~isempty (opt.funcVoxelDims) && length(opt.funcVoxelDims) ~= 3 + + errorStruct.identifier = 'checkOptions:voxDim'; + errorStruct.message = sprintf( ... + ['opt.funcVoxelDims should be a vector of length 3. '... + '\nCurrent value is: %d'], ... + opt.funcVoxelDims); + error(errorStruct); + + end + + opt = orderfields(opt); + +end + +function fieldsToSet = setDefaultOption() + % this defines the missing fields + + % group of subjects to analyze + fieldsToSet.groups = {''}; + % suject to run in each group + fieldsToSet.subjects = {[]}; + fieldsToSet.zeropad = 2; + + % session number and type of the anatomical reference + fieldsToSet.anatReference.type = 'T1w'; + fieldsToSet.anatReference.session = 1; + + % space where we conduct the analysis + fieldsToSet.space = 'MNI'; + + % The directory where the raw and derivatives are located + fieldsToSet.dataDir = ''; + fieldsToSet.derivativesDir = ''; + + % Options for slice time correction + % If left unspecified the slice timing will be done using the mid-volume acquisition + % time point as reference. + % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) + % instead of the slice index of the reference slice (the "SPM" way of doing things). + % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing + fieldsToSet.STC_referenceSlice = []; % reference slice: middle acquired slice + fieldsToSet.sliceOrder = []; % To be used if SPM can't extract slice info + + % fieldsToSet for normalize + % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. + fieldsToSet.funcVoxelDims = []; + + % specify the model file that contains the contrasts to compute + fieldsToSet.contrastList = {}; + fieldsToSet.model.file = ''; + + % specify the results to compute + fieldsToSet.result.Steps = struct( ... + 'Level', '', ... % dataset, run, subject + 'Contrasts', struct( ... + 'Name', '', ... + 'Mask', false, ... + 'MC', 'FWE', ... % FWE, none, FDR + 'p', 0.05, ... + 'k', 0, ... + 'NIDM', true)); + +end diff --git a/src/utils/loadAndCheckOptions.m b/src/utils/loadAndCheckOptions.m index 86f963f8..12fbae7c 100644 --- a/src/utils/loadAndCheckOptions.m +++ b/src/utils/loadAndCheckOptions.m @@ -19,13 +19,13 @@ return end - % finds most recent option file - if size(opt, 1) > 1 - containsDate = cellfun(@any, strfind(cellstr(opt), '_date-')); - opt = opt(containsDate, :); - opt = sortrows(opt); - opt = opt(end, :); - end + % finds most recent option file + if size(opt, 1) > 1 + containsDate = cellfun(@any, strfind(cellstr(opt), '_date-')); + opt = opt(containsDate, :); + opt = sortrows(opt); + opt = opt(end, :); + end if ischar(opt) && exist(opt, 'file') fprintf(1, '\nReading option from: %s.\n', opt); diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index 9c06d9ea..d4ac8af0 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -27,8 +27,8 @@ function bidsCopyRawFolder(opt, deleteZippedNii) end opt = loadAndCheckOptions(opt); - % Will only copy those modalities if they exist - % modalitiesToCopy = {'anat', 'func', 'fmap'}; + % Will only copy those modalities if they exist + % modalitiesToCopy = {'anat', 'func', 'fmap'}; %% All tasks in this experiment % raw directory and derivatives directory @@ -83,56 +83,56 @@ function bidsCopyRawFolder(opt, deleteZippedNii) mkdir(fullfile(derivativesDir, subDir, sessionFolder)); - % TODO - % implement copying specific modalities once bids-matlab can - % query modality for each session - % - % modalities = spm_BIDS(BIDS, 'modalities', ... - % 'sub', subID, ... - % 'ses', sessions{iSes}, ... - % 'task', opt.taskName); - % - % modalities = intersect(modalities, modalitiesToCopy); - - % for iModality = 1:numel(modalities) - % - % mkdir(fullfile(derivativesDir, subDir, sessionFolder, modalities{iModality})) - - % srcFolder = fullfile(rawDir, ... - % subDir, ... - % sessionFolder, ... - % modalities{iModality}); - % targetFolder = fullfile(derivativesDir, ... - % subDir, ... - % sessionFolder, ... - % modalities{iModality}); - - srcFolder = fullfile(rawDir, ... - subDir, ... - sessionFolder); - targetFolder = fullfile(derivativesDir, ... - subDir); - - try - status = system( ... - sprintf('cp -R -L -f %s %s', ... - srcFolder, ... - targetFolder)); - - if status > 0 - message = [ ... - 'Copying data with system command failed: ' ... - 'Are you running Windows?\n', ... - 'Will use matlab/octave copyfile command instead.\n', ... - 'Maybe your data set contains symbolic links' ... - '(e.g. if you use datalad or git-annex.']; - error(message); - end - - catch - fprintf(1, 'Using octave/matlab to copy files.'); - copyfile(srcFolder, targetFolder); - end + % TODO + % implement copying specific modalities once bids-matlab can + % query modality for each session + % + % modalities = spm_BIDS(BIDS, 'modalities', ... + % 'sub', subID, ... + % 'ses', sessions{iSes}, ... + % 'task', opt.taskName); + % + % modalities = intersect(modalities, modalitiesToCopy); + + % for iModality = 1:numel(modalities) + % + % mkdir(fullfile(derivativesDir, subDir, sessionFolder, modalities{iModality})) + + % srcFolder = fullfile(rawDir, ... + % subDir, ... + % sessionFolder, ... + % modalities{iModality}); + % targetFolder = fullfile(derivativesDir, ... + % subDir, ... + % sessionFolder, ... + % modalities{iModality}); + + srcFolder = fullfile(rawDir, ... + subDir, ... + sessionFolder); + targetFolder = fullfile(derivativesDir, ... + subDir); + + try + status = system( ... + sprintf('cp -R -L -f %s %s', ... + srcFolder, ... + targetFolder)); + + if status > 0 + message = [ ... + 'Copying data with system command failed: ' ... + 'Are you running Windows?\n', ... + 'Will use matlab/octave copyfile command instead.\n', ... + 'Maybe your data set contains symbolic links' ... + '(e.g. if you use datalad or git-annex.']; + error(message); + end + + catch + fprintf(1, 'Using octave/matlab to copy files.'); + copyfile(srcFolder, targetFolder); + end end diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index 93015a2d..d5090ec7 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -1,59 +1,59 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function bidsCreateVDM(opt) - % bidsCreateVDM(opt) - % - % inspired from spmup spmup_BIDS_preprocess (@ commit - % 198c980d6d7520b1a996f0e56269e2ceab72cc83) + % bidsCreateVDM(opt) + % + % inspired from spmup spmup_BIDS_preprocess (@ commit + % 198c980d6d7520b1a996f0e56269e2ceab72cc83) - if nargin < 1 - opt = []; - end - opt = loadAndCheckOptions(opt); - - % load the subjects/Groups information and the task name - [group, opt, BIDS] = getData(opt); + if nargin < 1 + opt = []; + end + opt = loadAndCheckOptions(opt); - fprintf(1, ' FIELDMAP WORKFLOW\n'); + % load the subjects/Groups information and the task name + [group, opt, BIDS] = getData(opt); - %% Loop through the groups, subjects, and sessions - for iGroup = 1:length(group) + fprintf(1, ' FIELDMAP WORKFLOW\n'); - groupName = group(iGroup).name; + %% Loop through the groups, subjects, and sessions + for iGroup = 1:length(group) - for iSub = 1:group(iGroup).numSub + groupName = group(iGroup).name; - subID = group(iGroup).subNumber{iSub}; + for iSub = 1:group(iGroup).numSub - % TODO Move to getInfo - types = spm_BIDS(BIDS, 'types', 'sub', subID); + subID = group(iGroup).subNumber{iSub}; - if any(ismember(types, {'phase12', 'phasediff', 'fieldmap', 'epi'})) + % TODO Move to getInfo + types = spm_BIDS(BIDS, 'types', 'sub', subID); - printProcessingSubject(groupName, iSub, subID); + if any(ismember(types, {'phase12', 'phasediff', 'fieldmap', 'epi'})) - fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); + printProcessingSubject(groupName, iSub, subID); - matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); - % - % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); + fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); - % spm_jobman('run', matlabbatch); + matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); + % + % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); - fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); + % spm_jobman('run', matlabbatch); - matlabbatch = setBatchCreateVDMs(opt, BIDS, subID); + fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); - saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID); + matlabbatch = setBatchCreateVDMs(opt, BIDS, subID); - % spm_jobman('run', matlabbatch); + saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID); - % TODO - % delete temporary mean images ?? + % spm_jobman('run', matlabbatch); - end + % TODO + % delete temporary mean images ?? - end + end end + + end end From 926af61da04a4ab2ef28517069bfc51aeb433059 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:08:35 +0100 Subject: [PATCH 11/40] make copyRaw deal with modalities --- src/workflows/bidsCopyRawFolder.m | 181 +++++++++++++++++------------- tests/test_bidsCopyRawFolder.m | 2 + 2 files changed, 102 insertions(+), 81 deletions(-) diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index d4ac8af0..2ff4d588 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -1,58 +1,54 @@ % (C) Copyright 2019 CPP BIDS SPM-pipeline developpers -function bidsCopyRawFolder(opt, deleteZippedNii) - % This function will copy the subject's folders from the "raw" folder to the - % "derivatives" folder, and will copy the dataset description and task json files +function bidsCopyRawFolder(opt, deleteZippedNii, modalitiesToCopy) + % + % This function will copy the subject's folders from the ``raw`` folder to the + % ``derivatives`` folder, and will copy the dataset description and task json files % to the derivatives directory. % Then it will search the derivatives directory for any zipped nii.gz image % and uncompress it to .nii images. % - % INPUT: + % USAGE:: + % + % bidsCopyRawFolder([opt,] ... + % [deleteZippedNii = true,] ... + % [modalitiesToCopy = {'anat', 'func', 'fmap'}]) % - % opt - options structure defined by the getOption function. If no inout is given - % this function will attempt to load a opt.mat file in the same directory - % to try to get some options + % :param opt: + % :type opt: type + % :param deleteZippedNii: + % :type deleteZippedNii: boolean + % :param modalitiesToCopy: + % :type modalitiesToCopy: cell % - % deleteZippedNii - true or false and will delete original zipped files - % after copying and unzipping %% input variables default values - if nargin < 2 % if second argument isn't specified - deleteZippedNii = 1; % delete the original zipped nii.gz + + if nargin < 3 || isempty(modalitiesToCopy) + % Will only copy those modalities if they exist + modalitiesToCopy = {'anat', 'func', 'fmap'}; + end + + if nargin < 2 || isempty(deleteZippedNii) + % delete the original zipped nii.gz + deleteZippedNii = true; end % if input has no opt, load the opt.mat file - if nargin < 1 + if nargin < 1 || isempty(opt) opt = []; end opt = loadAndCheckOptions(opt); - % Will only copy those modalities if they exist - % modalitiesToCopy = {'anat', 'func', 'fmap'}; - %% All tasks in this experiment % raw directory and derivatives directory rawDir = opt.dataDir; opt = setDerivativesDir(opt); derivativesDir = opt.derivativesDir; - % make derivatives folder if it doesnt exist - if ~exist(derivativesDir, 'dir') - mkdir(derivativesDir); - fprintf('derivatives directory created: %s \n', derivativesDir); - else - fprintf('derivatives directory already exists. \n'); - end + createDerivativeDir(derivativesDir); - % copy TSV and JSON file from raw folder if it doesnt exist - copyfile(fullfile(rawDir, '*.json'), derivativesDir); - fprintf(' json files copied to derivatives directory \n'); - - try - copyfile(fullfile(rawDir, '*.tsv'), derivativesDir); - fprintf(' tsv files copied to derivatives directory \n'); - catch - end + copyTsvJson(rawDir, derivativesDir); %% Loop through the groups, subjects, sessions [group, opt, BIDS] = getData(opt, rawDir); @@ -83,64 +79,86 @@ function bidsCopyRawFolder(opt, deleteZippedNii) mkdir(fullfile(derivativesDir, subDir, sessionFolder)); - % TODO - % implement copying specific modalities once bids-matlab can - % query modality for each session - % - % modalities = spm_BIDS(BIDS, 'modalities', ... - % 'sub', subID, ... - % 'ses', sessions{iSes}, ... - % 'task', opt.taskName); - % - % modalities = intersect(modalities, modalitiesToCopy); - - % for iModality = 1:numel(modalities) - % - % mkdir(fullfile(derivativesDir, subDir, sessionFolder, modalities{iModality})) - - % srcFolder = fullfile(rawDir, ... - % subDir, ... - % sessionFolder, ... - % modalities{iModality}); - % targetFolder = fullfile(derivativesDir, ... - % subDir, ... - % sessionFolder, ... - % modalities{iModality}); - - srcFolder = fullfile(rawDir, ... - subDir, ... - sessionFolder); - targetFolder = fullfile(derivativesDir, ... - subDir); - - try - status = system( ... - sprintf('cp -R -L -f %s %s', ... - srcFolder, ... - targetFolder)); - - if status > 0 - message = [ ... - 'Copying data with system command failed: ' ... - 'Are you running Windows?\n', ... - 'Will use matlab/octave copyfile command instead.\n', ... - 'Maybe your data set contains symbolic links' ... - '(e.g. if you use datalad or git-annex.']; - error(message); + modalities = bids.query(BIDS, 'modalities', ... + 'sub', subID, ... + 'ses', sessions{iSes}, ... + 'task', opt.taskName); + modalities = intersect(modalities, modalitiesToCopy); + + for iModality = 1:numel(modalities) + + mkdir(fullfile(derivativesDir, subDir, sessionFolder, modalities{iModality})); + + srcFolder = fullfile(rawDir, ... + subDir, ... + sessionFolder, ... + modalities{iModality}); + targetFolder = fullfile(derivativesDir, ... + subDir, ... + sessionFolder, ... + modalities{iModality}); + + try + status = system( ... + sprintf('cp -R -L -f %s %s', ... + srcFolder, ... + targetFolder)); + + if status > 0 + message = [ ... + 'Copying data with system command failed: ' ... + 'Are you running Windows?\n', ... + 'Will use matlab/octave copyfile command instead.\n', ... + 'Maybe your data set contains symbolic links' ... + '(e.g. if you use datalad or git-annex.']; + error(message); + end + + catch + fprintf(1, 'Using octave/matlab to copy files.'); + copyfile(srcFolder, targetFolder); end - catch - fprintf(1, 'Using octave/matlab to copy files.'); - copyfile(srcFolder, targetFolder); end + fprintf('folder copied: %s \n', subDir); + end + end - fprintf('folder copied: %s \n', subDir); + end - end + unzipFiles(derivativesDir, deleteZippedNii); + +end + +function createDerivativeDir(derivativesDir) + % make derivatives folder if it doesnt exist + + if ~exist(derivativesDir, 'dir') + mkdir(derivativesDir); + fprintf('derivatives directory created: %s \n', derivativesDir); + else + fprintf('derivatives directory already exists. \n'); end +end + +function copyTsvJson(rawDir, derivativesDir) + % copy TSV and JSON file from raw folder + + copyfile(fullfile(rawDir, '*.json'), derivativesDir); + fprintf(' json files copied to derivatives directory \n'); + + try + copyfile(fullfile(rawDir, '*.tsv'), derivativesDir); + fprintf(' tsv files copied to derivatives directory \n'); + catch + end + +end + +function unzipFiles(derivativesDir, deleteZippedNii) %% search for nifti files in a compressed nii.gz format zippedNiifiles = spm_select('FPListRec', derivativesDir, '^.*.nii.gz$'); @@ -152,9 +170,10 @@ function bidsCopyRawFolder(opt, deleteZippedNii) save_untouch_nii(n, file(1:end - 4)); % Save the functional data as unzipped nii fprintf('unzipped: %s \n', file); - if deleteZippedNii == 1 + if deleteZippedNii delete(file); % delete original zipped file end + end end diff --git a/tests/test_bidsCopyRawFolder.m b/tests/test_bidsCopyRawFolder.m index a82bd517..da2bb38b 100644 --- a/tests/test_bidsCopyRawFolder.m +++ b/tests/test_bidsCopyRawFolder.m @@ -7,6 +7,8 @@ end function test_bidsCopyRawFolderBasic() + + % TODO add test to only copy some modalities % directory with this script becomes the current directory opt.dataDir = fullfile( ... From 134e64e30a2fee5dbe4cb9b8afbd2c967b7df86e Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:14:06 +0100 Subject: [PATCH 12/40] refactor bidsCopyRaw --- src/workflows/bidsCopyRawFolder.m | 60 +++++++++++++++++-------------- tests/test_bidsCopyRawFolder.m | 2 +- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index 2ff4d588..694f16da 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -42,8 +42,8 @@ function bidsCopyRawFolder(opt, deleteZippedNii, modalitiesToCopy) %% All tasks in this experiment % raw directory and derivatives directory - rawDir = opt.dataDir; opt = setDerivativesDir(opt); + rawDir = opt.dataDir; derivativesDir = opt.derivativesDir; createDerivativeDir(derivativesDir); @@ -72,12 +72,12 @@ function bidsCopyRawFolder(opt, deleteZippedNii, modalitiesToCopy) for iSes = 1:nbSessions - sessionFolder = []; + sessionDir = []; if ~isempty(sessions{iSes}) - sessionFolder = ['ses-' sessions{iSes}]; + sessionDir = ['ses-' sessions{iSes}]; end - mkdir(fullfile(derivativesDir, subDir, sessionFolder)); + mkdir(fullfile(derivativesDir, subDir, sessionDir)); modalities = bids.query(BIDS, 'modalities', ... 'sub', subID, ... @@ -87,37 +87,18 @@ function bidsCopyRawFolder(opt, deleteZippedNii, modalitiesToCopy) for iModality = 1:numel(modalities) - mkdir(fullfile(derivativesDir, subDir, sessionFolder, modalities{iModality})); + mkdir(fullfile(derivativesDir, subDir, sessionDir, modalities{iModality})); srcFolder = fullfile(rawDir, ... subDir, ... - sessionFolder, ... + sessionDir, ... modalities{iModality}); targetFolder = fullfile(derivativesDir, ... subDir, ... - sessionFolder, ... + sessionDir, ... modalities{iModality}); - try - status = system( ... - sprintf('cp -R -L -f %s %s', ... - srcFolder, ... - targetFolder)); - - if status > 0 - message = [ ... - 'Copying data with system command failed: ' ... - 'Are you running Windows?\n', ... - 'Will use matlab/octave copyfile command instead.\n', ... - 'Maybe your data set contains symbolic links' ... - '(e.g. if you use datalad or git-annex.']; - error(message); - end - - catch - fprintf(1, 'Using octave/matlab to copy files.'); - copyfile(srcFolder, targetFolder); - end + copyModalityDir(srcFolder, targetFolder); end @@ -158,6 +139,31 @@ function copyTsvJson(rawDir, derivativesDir) end +function copyModalityDir(srcFolder, targetFolder) + + try + status = system( ... + sprintf('cp -R -L -f %s %s', ... + srcFolder, ... + targetFolder)); + + if status > 0 + message = [ ... + 'Copying data with system command failed: ' ... + 'Are you running Windows?\n', ... + 'Will use matlab/octave copyfile command instead.\n', ... + 'Maybe your data set contains symbolic links' ... + '(e.g. if you use datalad or git-annex.']; + error(message); + end + + catch + fprintf(1, 'Using octave/matlab to copy files.'); + copyfile(srcFolder, targetFolder); + end + +end + function unzipFiles(derivativesDir, deleteZippedNii) %% search for nifti files in a compressed nii.gz format zippedNiifiles = spm_select('FPListRec', derivativesDir, '^.*.nii.gz$'); diff --git a/tests/test_bidsCopyRawFolder.m b/tests/test_bidsCopyRawFolder.m index da2bb38b..fa559eea 100644 --- a/tests/test_bidsCopyRawFolder.m +++ b/tests/test_bidsCopyRawFolder.m @@ -7,7 +7,7 @@ end function test_bidsCopyRawFolderBasic() - + % TODO add test to only copy some modalities % directory with this script becomes the current directory From 4c71b0fe5bb6f89759a587cedbbb61a7597f928c Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:34:28 +0100 Subject: [PATCH 13/40] mh fix --- src/batches/setBatchComputeVDM.m | 4 +- src/batches/setBatchCreateVDMs.m | 4 +- src/utils/checkOptions.m | 162 +++++++++++++++---------------- 3 files changed, 84 insertions(+), 86 deletions(-) diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m index 903e9d2d..8731583b 100644 --- a/src/batches/setBatchComputeVDM.m +++ b/src/batches/setBatchComputeVDM.m @@ -27,8 +27,6 @@ 'FieldMap', ... 'T1.nii'); - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.mflags.template = {FM_template}; - UF = struct( ... 'method', 'Mark3D', ... 'fwhm', 10, ... @@ -36,7 +34,7 @@ 'ws', 1); MF = struct( ... - 'template', '', ... + 'template', {FM_template}, ... 'fwhm', 5, ... 'nerode', 2, ... 'ndilate', 4, ... diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m index a0ad651a..cad552df 100644 --- a/src/batches/setBatchCreateVDMs.m +++ b/src/batches/setBatchCreateVDMs.m @@ -77,8 +77,8 @@ totalReadoutTime = metadata.RepetitionTime; elseif isfield(metadata, 'EffectiveEchoSpacing') - totalReadoutTime = (metadata.NumberOfEchos - 1) ... - * metadata.EffectiveEchoSpacing; + totalReadoutTime = (metadata.NumberOfEchos - 1) * ... + metadata.EffectiveEchoSpacing; end end diff --git a/src/utils/checkOptions.m b/src/utils/checkOptions.m index 2b4eb57a..07f0e605 100644 --- a/src/utils/checkOptions.m +++ b/src/utils/checkOptions.m @@ -1,106 +1,106 @@ % (C) Copyright 2019 CPP BIDS SPM-pipeline developpers function opt = checkOptions(opt) - % opt = checkOptions(opt) - % - % we check the option inputs and add any missing field with some defaults + % opt = checkOptions(opt) + % + % we check the option inputs and add any missing field with some defaults - fieldsToSet = setDefaultOption(); + fieldsToSet = setDefaultOption(); - opt = setDefaultFields(opt, fieldsToSet); + opt = setDefaultFields(opt, fieldsToSet); - if ~isfield(opt, 'taskName') || isempty(opt.taskName) + if ~isfield(opt, 'taskName') || isempty(opt.taskName) - errorStruct.identifier = 'checkOptions:noTask'; - errorStruct.message = sprintf( ... - 'Provide the name of the task to analyze.'); - error(errorStruct); + errorStruct.identifier = 'checkOptions:noTask'; + errorStruct.message = sprintf( ... + 'Provide the name of the task to analyze.'); + error(errorStruct); - end + end - if ~all(cellfun(@ischar, opt.groups)) + if ~all(cellfun(@ischar, opt.groups)) - disp(opt.groups); + disp(opt.groups); - errorStruct.identifier = 'checkOptions:groupNotString'; - errorStruct.message = sprintf( ... - 'All group names should be string.'); - error(errorStruct); + errorStruct.identifier = 'checkOptions:groupNotString'; + errorStruct.message = sprintf( ... + 'All group names should be string.'); + error(errorStruct); - end + end - if ~isempty (opt.STC_referenceSlice) && length(opt.STC_referenceSlice) > 1 + if ~isempty (opt.STC_referenceSlice) && length(opt.STC_referenceSlice) > 1 - errorStruct.identifier = 'checkOptions:refSliceNotScalar'; - errorStruct.message = sprintf( ... - ['options.STC_referenceSlice should be a scalar.' ... - '\nCurrent value is: %d'], ... - opt.STC_referenceSlice); - error(errorStruct); + errorStruct.identifier = 'checkOptions:refSliceNotScalar'; + errorStruct.message = sprintf( ... + ['options.STC_referenceSlice should be a scalar.' ... + '\nCurrent value is: %d'], ... + opt.STC_referenceSlice); + error(errorStruct); - end + end - if ~isempty (opt.funcVoxelDims) && length(opt.funcVoxelDims) ~= 3 + if ~isempty (opt.funcVoxelDims) && length(opt.funcVoxelDims) ~= 3 - errorStruct.identifier = 'checkOptions:voxDim'; - errorStruct.message = sprintf( ... - ['opt.funcVoxelDims should be a vector of length 3. '... - '\nCurrent value is: %d'], ... - opt.funcVoxelDims); - error(errorStruct); + errorStruct.identifier = 'checkOptions:voxDim'; + errorStruct.message = sprintf( ... + ['opt.funcVoxelDims should be a vector of length 3. '... + '\nCurrent value is: %d'], ... + opt.funcVoxelDims); + error(errorStruct); - end + end - opt = orderfields(opt); + opt = orderfields(opt); end function fieldsToSet = setDefaultOption() - % this defines the missing fields - - % group of subjects to analyze - fieldsToSet.groups = {''}; - % suject to run in each group - fieldsToSet.subjects = {[]}; - fieldsToSet.zeropad = 2; - - % session number and type of the anatomical reference - fieldsToSet.anatReference.type = 'T1w'; - fieldsToSet.anatReference.session = 1; - - % space where we conduct the analysis - fieldsToSet.space = 'MNI'; - - % The directory where the raw and derivatives are located - fieldsToSet.dataDir = ''; - fieldsToSet.derivativesDir = ''; - - % Options for slice time correction - % If left unspecified the slice timing will be done using the mid-volume acquisition - % time point as reference. - % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) - % instead of the slice index of the reference slice (the "SPM" way of doing things). - % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing - fieldsToSet.STC_referenceSlice = []; % reference slice: middle acquired slice - fieldsToSet.sliceOrder = []; % To be used if SPM can't extract slice info - - % fieldsToSet for normalize - % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. - fieldsToSet.funcVoxelDims = []; - - % specify the model file that contains the contrasts to compute - fieldsToSet.contrastList = {}; - fieldsToSet.model.file = ''; - - % specify the results to compute - fieldsToSet.result.Steps = struct( ... - 'Level', '', ... % dataset, run, subject - 'Contrasts', struct( ... - 'Name', '', ... - 'Mask', false, ... - 'MC', 'FWE', ... % FWE, none, FDR - 'p', 0.05, ... - 'k', 0, ... - 'NIDM', true)); + % this defines the missing fields + + % group of subjects to analyze + fieldsToSet.groups = {''}; + % suject to run in each group + fieldsToSet.subjects = {[]}; + fieldsToSet.zeropad = 2; + + % session number and type of the anatomical reference + fieldsToSet.anatReference.type = 'T1w'; + fieldsToSet.anatReference.session = 1; + + % space where we conduct the analysis + fieldsToSet.space = 'MNI'; + + % The directory where the raw and derivatives are located + fieldsToSet.dataDir = ''; + fieldsToSet.derivativesDir = ''; + + % Options for slice time correction + % If left unspecified the slice timing will be done using the mid-volume acquisition + % time point as reference. + % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) + % instead of the slice index of the reference slice (the "SPM" way of doing things). + % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing + fieldsToSet.STC_referenceSlice = []; % reference slice: middle acquired slice + fieldsToSet.sliceOrder = []; % To be used if SPM can't extract slice info + + % fieldsToSet for normalize + % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. + fieldsToSet.funcVoxelDims = []; + + % specify the model file that contains the contrasts to compute + fieldsToSet.contrastList = {}; + fieldsToSet.model.file = ''; + + % specify the results to compute + fieldsToSet.result.Steps = struct( ... + 'Level', '', ... % dataset, run, subject + 'Contrasts', struct( ... + 'Name', '', ... + 'Mask', false, ... + 'MC', 'FWE', ... % FWE, none, FDR + 'p', 0.05, ... + 'k', 0, ... + 'NIDM', true)); end From d81b6f34ad2372d114c2d35a950af232ed8dd77e Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:35:52 +0100 Subject: [PATCH 14/40] update function templates --- getOption_template | 47 ---------------- src/templates/templateFunction.m | 48 ++++++++-------- src/templates/templateFunctionExample.m | 74 ++++++++++++------------- src/templates/templateFunctionNumpy.m | 32 +++++------ src/templates/templateGetOption.m | 47 ++++++++++++++++ 5 files changed, 124 insertions(+), 124 deletions(-) delete mode 100644 getOption_template create mode 100644 src/templates/templateGetOption.m diff --git a/getOption_template b/getOption_template deleted file mode 100644 index 15be3d13..00000000 --- a/getOption_template +++ /dev/null @@ -1,47 +0,0 @@ -function opt = getOption() -% returns a structure that contains the options chosen by the user to return -% the different workflows - -if nargin<1 - opt = []; -end - -% group of subjects to analyze -opt.groups = {''}; -% suject to run in each group -opt.subjects = {[]}; - -% task to analyze -opt.taskName = 'balloonanalogrisktask'; - -% The directory where the data are located -opt.dataDir = '/home/remi/BIDS/ds001/rawdata'; - -% Options for slice time correction -% If left unspecified the slice timing will be done using the mid-volume acquisition -% time point as reference. -% Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) -% instead of the slice index of the reference slice (the "SPM" way of doing things). -% More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing -opt.sliceOrder = []; -opt.STC_referenceSlice = []; - -% Options for normalize -% Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. -opt.funcVoxelDims = []; - -% specify the model file that contains the contrasts to compute -opt.model.univariate.file = fullfile(fileparts(mfilename('fullpath')), 'model', model-balloonanalogriskUnivariate_smdl.json'); - -% specify the result to compute -opt.result.Steps(1) = struct(... - 'Level', 'dataset', ... - 'Contrasts', struct(... - 'Name', 'pumps_demean', ... % has to match - 'Mask', false, ... % this might need improving if a mask is required - 'MC', 'none', ... FWE, none, FDR - 'p', 0.05, ... - 'k', 0, ... - 'NIDM', true) ); - -end diff --git a/src/templates/templateFunction.m b/src/templates/templateFunction.m index bf349d86..4b6135cc 100644 --- a/src/templates/templateFunction.m +++ b/src/templates/templateFunction.m @@ -1,29 +1,29 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [argout1, argout2] = templateFunction(argin1, argin2, argin3) - % - % Short description of what the function does goes here. - % - % USAGE: - % - % [argout1, argout2] = templateFunction(argin1, [argin2 == default,] [argin3]) - % - % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, - % consectetur adipiscing elit. Ut congue nec est ac lacinia. - % :type argin1: type - % :param argin2: optional argument and its default value. And some of the - % options can be shown in litteral like ``this`` or ``that``. - % :type argin2: string - % :param argin3: (dimension) optional argument - % - % :returns: - :argout1: (type) (dimension) - % - :argout2: (type) (dimension) - % - % .. todo: - % - % - item 1 - % - item 2 + % + % Short description of what the function does goes here. + % + % USAGE:: + % + % [argout1, argout2] = templateFunction(argin1, [argin2 == default,] [argin3]) + % + % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, + % consectetur adipiscing elit. Ut congue nec est ac lacinia. + % :type argin1: type + % :param argin2: optional argument and its default value. And some of the + % options can be shown in litteral like ``this`` or ``that``. + % :type argin2: string + % :param argin3: (dimension) optional argument + % + % :returns: - :argout1: (type) (dimension) + % - :argout2: (type) (dimension) + % + % .. todo: + % + % - item 1 + % - item 2 - % The code goes below + % The code goes below -end \ No newline at end of file +end diff --git a/src/templates/templateFunctionExample.m b/src/templates/templateFunctionExample.m index 0ce1dec7..e4a39d6d 100644 --- a/src/templates/templateFunctionExample.m +++ b/src/templates/templateFunctionExample.m @@ -1,42 +1,42 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function templateFunctionExample() - % This function illustrates a documentation test defined for MOdox. - % Other than that it does absolutely nothinghort description of what - % the function does goes here. - % - % Examples: - % a=2; - % disp(a) - % % Expected output is prefixed by '%||' as in the following line: - % %|| 2 - % % - % % The test continues because no interruption through whitespace, - % % as the previous line used a '%' comment character; - % % thus the 'a' variable is still in the namespace and can be - % % accessed. - % b=3+a; - % disp(a+[3 4]) - % %|| [5 6] - % - % % A new test starts here because the previous line was white-space - % % only. Thus the 'a' and 'b' variables are not present here anymore. - % % The following expression raises an error because the 'b' variable - % % is not defined (and does not carry over from the previous test). - % % Because the expected output indicates an error as well, - % % the test passes - % disp(b) - % %|| error('Some error') - % - % % A set of expressions is ignored if there is no expected output - % % (that is, no lines starting with '%||'). - % % Thus, the following expression is not part of any test, - % % and therefore does not raise an error. - % error('this is never executed) - % - % - % % tests end here because test indentation has ended + % This function illustrates a documentation test defined for MOdox. + % Other than that it does absolutely nothinghort description of what + % the function does goes here. + % + % Examples: + % a=2; + % disp(a) + % % Expected output is prefixed by '%||' as in the following line: + % %|| 2 + % % + % % The test continues because no interruption through whitespace, + % % as the previous line used a '%' comment character; + % % thus the 'a' variable is still in the namespace and can be + % % accessed. + % b=3+a; + % disp(a+[3 4]) + % %|| [5 6] + % + % % A new test starts here because the previous line was white-space + % % only. Thus the 'a' and 'b' variables are not present here anymore. + % % The following expression raises an error because the 'b' variable + % % is not defined (and does not carry over from the previous test). + % % Because the expected output indicates an error as well, + % % the test passes + % disp(b) + % %|| error('Some error') + % + % % A set of expressions is ignored if there is no expected output + % % (that is, no lines starting with '%||'). + % % Thus, the following expression is not part of any test, + % % and therefore does not raise an error. + % error('this is never executed) + % + % + % % tests end here because test indentation has ended - % The code goes below + % The code goes below -end \ No newline at end of file +end diff --git a/src/templates/templateFunctionNumpy.m b/src/templates/templateFunctionNumpy.m index 77cb0e67..ff449f9d 100644 --- a/src/templates/templateFunctionNumpy.m +++ b/src/templates/templateFunctionNumpy.m @@ -1,21 +1,21 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [argout] = templateFunctionNumpy(argin1, argin2, argin3) - % - % Short description of what the function does goes here. - % - % y = templateFunction(argin1, argin2, argin3) - % - % Parameters: - % argin1: The first input value - % - % argin2: The second input value - % - % argin3: The third input value - % - % Returns: - % The input value multiplied by two + % + % Short description of what the function does goes here. + % + % y = templateFunction(argin1, argin2, argin3) + % + % Parameters: + % argin1: The first input value + % + % argin2: The second input value + % + % argin3: The third input value + % + % Returns: + % The input value multiplied by two - % The code goes below + % The code goes below -end \ No newline at end of file +end diff --git a/src/templates/templateGetOption.m b/src/templates/templateGetOption.m new file mode 100644 index 00000000..38e85221 --- /dev/null +++ b/src/templates/templateGetOption.m @@ -0,0 +1,47 @@ +function opt = templateGetOption() + % returns a structure that contains the options chosen by the user to return + % the different workflows + + if nargin < 1 + opt = []; + end + + % group of subjects to analyze + opt.groups = {''}; + % suject to run in each group + opt.subjects = {[]}; + + % task to analyze + opt.taskName = 'balloonanalogrisktask'; + + % The directory where the data are located + opt.dataDir = '/home/remi/BIDS/ds001/rawdata'; + + % Options for slice time correction + % If left unspecified the slice timing will be done using the mid-volume acquisition + % time point as reference. + % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) + % instead of the slice index of the reference slice (the "SPM" way of doing things). + % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing + opt.sliceOrder = []; + opt.STC_referenceSlice = []; + + % Options for normalize + % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. + opt.funcVoxelDims = []; + + % specify the model file that contains the contrasts to compute + opt.model.univariate.file = fullfile(fileparts(mfilename('fullpath')), 'model', model - balloonanalogriskUnivariate_smdl.json'); + + % specify the result to compute + opt.result.Steps(1) = struct( ... + 'Level', 'dataset', ... + 'Contrasts', struct( ... + 'Name', 'pumps_demean', ... % has to match + 'Mask', false, ... % this might need improving if a mask is required + 'MC', 'none', ... FWE, none, FDR + 'p', 0.05, ... + 'k', 0, ... + 'NIDM', true)); + +end From a2b4a90de45399dea14c06914563837a2d8d5f82 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:49:01 +0100 Subject: [PATCH 15/40] mh fix --- src/batches/setBatchRealign.m | 27 ++++++++++++++++++++++----- src/templates/templateGetOption.m | 11 ++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/batches/setBatchRealign.m b/src/batches/setBatchRealign.m index cc6c9f33..fbe62787 100644 --- a/src/batches/setBatchRealign.m +++ b/src/batches/setBatchRealign.m @@ -1,11 +1,28 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers -function [matlabbatch, voxDim] = setBatchRealign(matlabbatch, BIDS, subID, opt, action) - % [matlabbatch, voxDim] = setBatchRealign(matlabbatch, BIDS, subID, opt, action) +function [matlabbatch, voxDim] = setBatchRealign(varargin) % - % to set the batch in a spatial preprocessing pipeline + % Set the batch for realign / realign and reslice / realign and unwarp % - % Assumption about the order of the sessions: + % USAGE:: + % + % [matlabbatch, voxDim] = setBatchRealign(matlabbatch, BIDS, subID, opt, [action = 'realign']) + % + % :param matlabbatch: SPM batch + % :type matlabbatch: structure + % :param BIDS: BIDS layout returned by ``getData`` + % :type BIDS: structure + % :param subID: subject label + % :type subID: string + % :param opt: options + % :type opt: structure + % :param action: ``realign``, ``realignReslice``, ``realignUnwarp`` + % :type action: string + % + % :returns: - :matlabbatch: (structure) (dimension) + % - :voxDim: (array) (dimension) + + [matlabbatch, BIDS, subID, opt, action] = deal(varargin{:}); if nargin < 5 || isempty(action) action = 'realign'; diff --git a/src/templates/templateGetOption.m b/src/templates/templateGetOption.m index 38e85221..a54ac336 100644 --- a/src/templates/templateGetOption.m +++ b/src/templates/templateGetOption.m @@ -1,3 +1,5 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + function opt = templateGetOption() % returns a structure that contains the options chosen by the user to return % the different workflows @@ -31,14 +33,17 @@ opt.funcVoxelDims = []; % specify the model file that contains the contrasts to compute - opt.model.univariate.file = fullfile(fileparts(mfilename('fullpath')), 'model', model - balloonanalogriskUnivariate_smdl.json'); + opt.model.univariate.file = ... + fullfile(fileparts(mfilename('fullpath')), ... + 'model', ... + 'model - balloonanalogriskUnivariate_smdl.json'); % specify the result to compute opt.result.Steps(1) = struct( ... 'Level', 'dataset', ... 'Contrasts', struct( ... - 'Name', 'pumps_demean', ... % has to match - 'Mask', false, ... % this might need improving if a mask is required + 'Name', 'pumps_demean', ... + 'Mask', false, ... 'MC', 'none', ... FWE, none, FDR 'p', 0.05, ... 'k', 0, ... From 53897560daa3a421432a388d5f968999c6dbe384 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:51:05 +0100 Subject: [PATCH 16/40] remove change log (for now) --- CHANGELOG.md | 178 --------------------------------------------------- 1 file changed, 178 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a63e8576..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,178 +0,0 @@ -# Changelog - -## [Unreleased](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/tree/HEAD) - -[Full Changelog](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/compare/v0.0.3...HEAD) - -**Closed issues:** - -- add code coverage - [\#77](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/77) -- getData :Accommidate scripts for structural analysis \(without funtional\). - [\#68](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/68) - -**Merged pull requests:** - -- major refactoring reporting for duty - [\#79](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/79) - ([Remi-Gau](https://github.com/Remi-Gau)) -- fix linter and use Mox unit for testing - [\#76](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/76) - ([Remi-Gau](https://github.com/Remi-Gau)) -- reorganize and set up MH linter - [\#75](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/75) - ([Remi-Gau](https://github.com/Remi-Gau)) -- add octave in dependencies - [\#71](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/71) - ([Remi-Gau](https://github.com/Remi-Gau)) -- allow getData to query only anatomical data - [\#69](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/69) - ([Remi-Gau](https://github.com/Remi-Gau)) -- update print credit - [\#63](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/63) - ([Remi-Gau](https://github.com/Remi-Gau)) -- update DOI - [\#62](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/62) - ([Remi-Gau](https://github.com/Remi-Gau)) -- update DOI badge - [\#61](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/61) - ([Remi-Gau](https://github.com/Remi-Gau)) - -## [v0.0.3](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/tree/v0.0.3) (2019-11-28) - -[Full Changelog](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/compare/v0.0.2...v0.0.3) - -**Merged pull requests:** - -- fix RFX issues and add credits - [\#60](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/60) - ([Remi-Gau](https://github.com/Remi-Gau)) - -## [v0.0.2](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/tree/v0.0.2) (2019-11-26) - -[Full Changelog](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/compare/v0.0.1...v0.0.2) - -**Merged pull requests:** - -- Create LICENSE - [\#58](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/58) - ([Remi-Gau](https://github.com/Remi-Gau)) -- fix problem when task JSON file is missing from root folder - [\#56](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/56) - ([Remi-Gau](https://github.com/Remi-Gau)) - -## [v0.0.1](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/tree/v0.0.1) (2019-11-13) - -[Full Changelog](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/compare/8ca4e1b47af1d983cde988bae1a9bd9659151330...v0.0.1) - -**Implemented enhancements:** - -- refactor getRuns, getSessions, getFilenames - [\#9](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/9) -- find a way to specify the contrasts to compute - [\#11](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/11) -- Complete boiler plate methods section of the README - [\#17](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/17) - ([Remi-Gau](https://github.com/Remi-Gau)) -- Big PR to fix previous PR, update FFX and RFX - [\#2](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/2) - ([Remi-Gau](https://github.com/Remi-Gau)) - -**Closed issues:** - -- spm_jsonread issue - [\#54](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/54) -- \[WORKSHOP 2019\] - to do list - [\#48](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/48) -- \[WORKSHOP 2019\] - MVPA - [\#47](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/47) -- \[WORKSHOP 2019\] - sharing data - [\#46](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/46) -- changes way subjects are indexed in getData - [\#44](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/44) -- Add Moh as contributors - [\#37](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/37) -- add Marco as contributors - [\#35](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/35) -- getData issue with 2 groups - [\#22](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/22) -- create a function to checks options and set some defaults if none are - specified - [\#15](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/15) -- use SPM BIDS data set from SPM tuto to test and make a simple demo - [\#7](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/7) -- test with octave - [\#5](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/5) -- export final results as NIDM results to facilitate upload to neurovault - [\#52](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/52) -- Consider to unzip the bold files in a separate function from BIDS_rmDumies.m - [\#26](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/26) -- create a function that copies the raw data into a derivatives/SPM12-CPP - directory - [\#25](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/issues/25) - -**Merged pull requests:** - -- BIDS results - [\#55](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/55) - ([Remi-Gau](https://github.com/Remi-Gau)) -- \[WIP\] constrast specification + uni and multivaraite issues + remove - dummies [\#51](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/51) - ([Remi-Gau](https://github.com/Remi-Gau)) -- fix some details in getData to pass tests and make octave compatible - [\#45](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/45) - ([Remi-Gau](https://github.com/Remi-Gau)) -- fix indexing issue in getData - [\#43](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/43) - ([Remi-Gau](https://github.com/Remi-Gau)) -- docs: add OliColli as a contributor - [\#40](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/40) - ([allcontributors[bot]](https://github.com/apps/allcontributors)) -- docs: add anege as a contributor - [\#39](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/39) - ([allcontributors[bot]](https://github.com/apps/allcontributors)) -- docs: add mohmdrezk as a contributor - [\#38](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/38) - ([allcontributors[bot]](https://github.com/apps/allcontributors)) -- docs: add marcobarilari as a contributor - [\#36](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/36) - ([allcontributors[bot]](https://github.com/apps/allcontributors)) -- docs: add Remi-Gau as a contributor - [\#30](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/30) - ([allcontributors[bot]](https://github.com/apps/allcontributors)) -- Add another test to getData and improve README - [\#28](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/28) - ([Remi-Gau](https://github.com/Remi-Gau)) -- edit info about step1: 'Remove Dummy Scans' - [\#27](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/27) - ([marcobarilari](https://github.com/marcobarilari)) -- update doc - [\#24](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/24) - ([Remi-Gau](https://github.com/Remi-Gau)) -- - fix getData issue when there are multiple groups - [\#23](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/23) - ([mohmdrezk](https://github.com/mohmdrezk)) -- Ane getsessions merge - [\#19](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/19) - ([anege](https://github.com/anege)) -- Dockerfile and option defaults - [\#16](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/16) - ([Remi-Gau](https://github.com/Remi-Gau)) -- Containerization of the pipeline with docker and octave - [\#13](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/13) - ([Remi-Gau](https://github.com/Remi-Gau)) -- fix crash on batch_download_run - [\#12](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/12) - ([Remi-Gau](https://github.com/Remi-Gau)) -- Initial preparartion for Workshop scripts for Motion dataset - [\#10](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/10) - ([mohmdrezk](https://github.com/mohmdrezk)) -- delete extra getOptions - [\#8](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/8) - ([Remi-Gau](https://github.com/Remi-Gau)) -- Split into 3 repos - [\#1](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/pull/1) - ([Remi-Gau](https://github.com/Remi-Gau)) - -\* _This Changelog was automatically generated by -[github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_ From 56ba1f4ca7ac7b740da1e652cf77f64260f6e021 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 13:55:04 +0100 Subject: [PATCH 17/40] update README --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d282e2c1..9c20804e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ + + **Code quality and style** [![](https://img.shields.io/badge/Octave-CI-blue?logo=Octave&logoColor=white)](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/actions) @@ -20,6 +22,8 @@ + + # CPPL SPM12 Pipeline This is a set of functions to fMRI analysis on a @@ -29,9 +33,9 @@ This can perform: - slice timing correction, - spatial preprocessing: - - realignment, coregistration `func` to `anat`, `anat` segmention, + - realignment, coregistration `func` to `anat`, `anat` segmentation, normalization to MNI space - - realignm and unwarp, coregistration `func` to `anat`, `anat` segmention + - realignm and unwarp, coregistration `func` to `anat`, `anat` segmentation - smoothing, - Quality analysis: - for anatomical data @@ -61,13 +65,14 @@ For instructions see the following links: | [spmup](https://github.com/CPernet/spmup) | NA | -## Contributors ✨ +## Contributors Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + From e068b83d2654f8a11c0036168518bbe3f8619f41 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 14:01:32 +0100 Subject: [PATCH 18/40] fix bidsCopyRawFolder --- src/workflows/bidsCopyRawFolder.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index 694f16da..6f059559 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -95,8 +95,7 @@ function bidsCopyRawFolder(opt, deleteZippedNii, modalitiesToCopy) modalities{iModality}); targetFolder = fullfile(derivativesDir, ... subDir, ... - sessionDir, ... - modalities{iModality}); + sessionDir); copyModalityDir(srcFolder, targetFolder); From 433daff473dda841cdce3c51400df6e56db212be Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 14:12:56 +0100 Subject: [PATCH 19/40] rename coregistration batch setting functions --- src/batches/setBatchCoregistration.m | 86 +++---------------- src/batches/setBatchCoregistrationFmap.m | 3 + .../setBatchCoregistrationFuncToAnat.m | 76 ++++++++++++++++ src/batches/setBatchCoregistrationGeneral.m | 14 --- src/workflows/bidsSpatialPrepro.m | 2 +- 5 files changed, 92 insertions(+), 89 deletions(-) create mode 100644 src/batches/setBatchCoregistrationFuncToAnat.m delete mode 100644 src/batches/setBatchCoregistrationGeneral.m diff --git a/src/batches/setBatchCoregistration.m b/src/batches/setBatchCoregistration.m index a9438f8c..5cb17502 100644 --- a/src/batches/setBatchCoregistration.m +++ b/src/batches/setBatchCoregistration.m @@ -1,76 +1,14 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers - -function matlabbatch = setBatchCoregistration(matlabbatch, BIDS, subID, opt) - - fprintf(1, ' BUILDING SPATIAL JOB : COREGISTER\n'); - - matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref(1) = ... - cfg_dep('Named File Selector: Anatomical(1) - Files', ... - substruct( ... - '.', 'val', '{}', {opt.orderBatches.selectAnat}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct('.', 'files', '{}', {1})); - - % SOURCE IMAGE : DEPENDENCY FROM REALIGNEMENT - % Mean Image - - meanImageToUse = 'rmean'; - otherImageToUse = 'cfiles'; - if strcmp(opt.space, 'individual') - meanImageToUse = 'meanuwr'; - otherImageToUse = 'uwrfiles'; - end - - matlabbatch{end}.spm.spatial.coreg.estimate.source(1) = ... - cfg_dep('Realign: Estimate & Reslice/Unwarp: Mean Image', ... - substruct( ... - '.', 'val', '{}', {opt.orderBatches.realign}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct('.', meanImageToUse)); - - % OTHER IMAGES : DEPENDENCY FROM REALIGNEMENT - - [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); - - runCounter = 1; - - for iSes = 1:nbSessions - - % get all runs for that subject for this session - [~, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes}); - - for iRun = 1:nbRuns - - matlabbatch{end}.spm.spatial.coreg.estimate.other(runCounter) = ... - cfg_dep([ ... - 'Realign: Estimate & Reslice/Unwarp: Realigned Images (Sess ', ... - num2str(runCounter), ... - ')'], ... - substruct( ... - '.', 'val', '{}', {opt.orderBatches.realign}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct( ... - '.', 'sess', '()', {runCounter}, ... - '.', otherImageToUse)); - - runCounter = runCounter + 1; - - end - - end - - % The following lines are commented out because those parameters - % can be set in the spm_my_defaults.m - % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.cost_fun = 'nmi'; - % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.sep = [4 2]; - % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.tol = ... - % [0.02 0.02 0.02 0.001 0.001 0.001 0.01 0.01 0.01 0.001 0.001 0.001]; - % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.fwhm = [7 7]; +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchCoregistration(matlabbatch, ref, src, other) + % matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) + % + % ref: string + % src: string + % other: cell string + + matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref = { ref }; + matlabbatch{end}.spm.spatial.coreg.estimate.source = { src }; + matlabbatch{end}.spm.spatial.coreg.estimate.other = other; end diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m index 9e11206f..3a0dc45b 100644 --- a/src/batches/setBatchCoregistrationFmap.m +++ b/src/batches/setBatchCoregistrationFmap.m @@ -6,6 +6,9 @@ % assumes all the fieldmap relate to the current task % - use the "for" metadata field % - implement for 'phase12', 'fieldmap', 'epi' + + + fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); % Create rough mean of the 1rst run to improve SNR for coregistration % TODO use the slice timed EPI if STC was used ? diff --git a/src/batches/setBatchCoregistrationFuncToAnat.m b/src/batches/setBatchCoregistrationFuncToAnat.m new file mode 100644 index 00000000..40e11635 --- /dev/null +++ b/src/batches/setBatchCoregistrationFuncToAnat.m @@ -0,0 +1,76 @@ +% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt) + + fprintf(1, ' BUILDING SPATIAL JOB : COREGISTER\n'); + + matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref(1) = ... + cfg_dep('Named File Selector: Anatomical(1) - Files', ... + substruct( ... + '.', 'val', '{}', {opt.orderBatches.selectAnat}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct('.', 'files', '{}', {1})); + + % SOURCE IMAGE : DEPENDENCY FROM REALIGNEMENT + % Mean Image + + meanImageToUse = 'rmean'; + otherImageToUse = 'cfiles'; + if strcmp(opt.space, 'individual') + meanImageToUse = 'meanuwr'; + otherImageToUse = 'uwrfiles'; + end + + matlabbatch{end}.spm.spatial.coreg.estimate.source(1) = ... + cfg_dep('Realign: Estimate & Reslice/Unwarp: Mean Image', ... + substruct( ... + '.', 'val', '{}', {opt.orderBatches.realign}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct('.', meanImageToUse)); + + % OTHER IMAGES : DEPENDENCY FROM REALIGNEMENT + + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + + runCounter = 1; + + for iSes = 1:nbSessions + + % get all runs for that subject for this session + [~, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes}); + + for iRun = 1:nbRuns + + matlabbatch{end}.spm.spatial.coreg.estimate.other(runCounter) = ... + cfg_dep([ ... + 'Realign: Estimate & Reslice/Unwarp: Realigned Images (Sess ', ... + num2str(runCounter), ... + ')'], ... + substruct( ... + '.', 'val', '{}', {opt.orderBatches.realign}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct( ... + '.', 'sess', '()', {runCounter}, ... + '.', otherImageToUse)); + + runCounter = runCounter + 1; + + end + + end + + % The following lines are commented out because those parameters + % can be set in the spm_my_defaults.m + % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.cost_fun = 'nmi'; + % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.sep = [4 2]; + % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.tol = ... + % [0.02 0.02 0.02 0.001 0.001 0.001 0.01 0.01 0.01 0.001 0.001 0.001]; + % matlabbatch{end}.spm.spatial.coreg.estimate.eoptions.fwhm = [7 7]; + +end diff --git a/src/batches/setBatchCoregistrationGeneral.m b/src/batches/setBatchCoregistrationGeneral.m deleted file mode 100644 index 72c9fc24..00000000 --- a/src/batches/setBatchCoregistrationGeneral.m +++ /dev/null @@ -1,14 +0,0 @@ -% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers - -function matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) - % matlabbatch = setBatchCoregistrationGeneral(matlabbatch, ref, src, other) - % - % ref: string - % src: string - % other: cell string - - matlabbatch{end + 1}.spm.spatial.coreg.estimate.ref = { ref }; - matlabbatch{end}.spm.spatial.coreg.estimate.source = { src }; - matlabbatch{end}.spm.spatial.coreg.estimate.other = other; - -end diff --git a/src/workflows/bidsSpatialPrepro.m b/src/workflows/bidsSpatialPrepro.m index 2a795945..2a100219 100644 --- a/src/workflows/bidsSpatialPrepro.m +++ b/src/workflows/bidsSpatialPrepro.m @@ -57,7 +57,7 @@ function bidsSpatialPrepro(opt) opt.orderBatches.realign = 2; % dependency from file selector ('Anatomical') - matlabbatch = setBatchCoregistration(matlabbatch, BIDS, subID, opt); + matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt); opt.orderBatches.coregister = 3; matlabbatch = setBatchSaveCoregistrationMatrix(matlabbatch, BIDS, subID, opt); From 4d109c12c5c11d4e34e8c3b137a7b26f86602b7c Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 15:18:18 +0100 Subject: [PATCH 20/40] fix copyright year --- demos/openneuro/ds001168_getOption.m | 2 +- src/QA/anatomicalQA.m | 2 +- src/QA/functionalQA.m | 2 +- src/batches/setBatchComputeVDM.m | 2 +- src/batches/setBatchReslice.m | 2 +- src/batches/setBatchSaveCoregistrationMatrix.m | 2 +- src/mancoreg/mancoreg.m | 2 +- src/mancoreg/mancoregCallbacks.m | 2 +- src/utils/createDataDictionary.m | 2 +- src/utils/loadAndCheckOptions.m | 2 +- src/utils/saveOptions.m | 2 +- src/workflows/bidsRealignUnwarp.m | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/demos/openneuro/ds001168_getOption.m b/demos/openneuro/ds001168_getOption.m index c85d66f3..3ef84b25 100644 --- a/demos/openneuro/ds001168_getOption.m +++ b/demos/openneuro/ds001168_getOption.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function opt = ds001168_getOption() % returns a structure that contains the options chosen by the user to run diff --git a/src/QA/anatomicalQA.m b/src/QA/anatomicalQA.m index 0c3eb686..c8336812 100644 --- a/src/QA/anatomicalQA.m +++ b/src/QA/anatomicalQA.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function anatomicalQA(opt) % anatomicalQA(opt) diff --git a/src/QA/functionalQA.m b/src/QA/functionalQA.m index baf04c41..ed016a9b 100644 --- a/src/QA/functionalQA.m +++ b/src/QA/functionalQA.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function functionalQA(opt) % functionalQA(opt) diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m index 8731583b..25eaece7 100644 --- a/src/batches/setBatchComputeVDM.m +++ b/src/batches/setBatchComputeVDM.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchComputeVDM(matlabbatch, fmapType) % matlabbatch = setBatchComputeVDM(type) diff --git a/src/batches/setBatchReslice.m b/src/batches/setBatchReslice.m index 3e270b8a..3de4a142 100644 --- a/src/batches/setBatchReslice.m +++ b/src/batches/setBatchReslice.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchReslice(referenceImg, sourceImages) % matlabbatch = bidsSmoothing(referenceImg, sourceImages) diff --git a/src/batches/setBatchSaveCoregistrationMatrix.m b/src/batches/setBatchSaveCoregistrationMatrix.m index 78ab40da..99e70647 100644 --- a/src/batches/setBatchSaveCoregistrationMatrix.m +++ b/src/batches/setBatchSaveCoregistrationMatrix.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function matlabbatch = setBatchSaveCoregistrationMatrix(matlabbatch, BIDS, subID, opt) diff --git a/src/mancoreg/mancoreg.m b/src/mancoreg/mancoreg.m index e4e2d99d..d2cf31e7 100644 --- a/src/mancoreg/mancoreg.m +++ b/src/mancoreg/mancoreg.m @@ -1,7 +1,7 @@ % (C) Copyright 2004-2009 JH % (C) Copyright 2009_2012 DSS % (C) Copyright 2012_2019 Remi Gau -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function mancoreg(targetimage, sourceimage) % mancoreg(targetimage, sourceimage) diff --git a/src/mancoreg/mancoregCallbacks.m b/src/mancoreg/mancoregCallbacks.m index 32f42b63..f1bb63f0 100644 --- a/src/mancoreg/mancoregCallbacks.m +++ b/src/mancoreg/mancoregCallbacks.m @@ -1,7 +1,7 @@ % (C) Copyright 2004-2009 JH % (C) Copyright 2009_2012 DSS % (C) Copyright 2012_2019 Remi Gau -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function mancoregCallbacks(operation, varargin) % mancoreg_callbacks(op, varargin) diff --git a/src/utils/createDataDictionary.m b/src/utils/createDataDictionary.m index c58db070..454f6edb 100644 --- a/src/utils/createDataDictionary.m +++ b/src/utils/createDataDictionary.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function createDataDictionary(subFuncDataDir, fileName, nbColums) diff --git a/src/utils/loadAndCheckOptions.m b/src/utils/loadAndCheckOptions.m index 12fbae7c..619b497a 100644 --- a/src/utils/loadAndCheckOptions.m +++ b/src/utils/loadAndCheckOptions.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function opt = loadAndCheckOptions(opt) % opt = loadAndCheckOptions(opt) diff --git a/src/utils/saveOptions.m b/src/utils/saveOptions.m index dff2d99b..4d912caa 100644 --- a/src/utils/saveOptions.m +++ b/src/utils/saveOptions.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function saveOptions(opt) diff --git a/src/workflows/bidsRealignUnwarp.m b/src/workflows/bidsRealignUnwarp.m index 40fe98cd..b3857c06 100644 --- a/src/workflows/bidsRealignUnwarp.m +++ b/src/workflows/bidsRealignUnwarp.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function bidsRealignUnwarp(opt) % bidsRealignReslice(opt) From f62d760466ba44c7fc3fb2ccae8f096eb66bf4f9 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 15:27:43 +0100 Subject: [PATCH 21/40] upate VDM workflow --- src/batches/setBatchComputeVDM.m | 20 +++++++++++--------- src/batches/setBatchCoregistrationFmap.m | 5 ++--- src/batches/setBatchCreateVDMs.m | 9 ++++++++- src/workflows/bidsCreateVDM.m | 16 ++++------------ 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m index 25eaece7..49bd1f14 100644 --- a/src/batches/setBatchComputeVDM.m +++ b/src/batches/setBatchComputeVDM.m @@ -1,6 +1,6 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers -function matlabbatch = setBatchComputeVDM(matlabbatch, fmapType) +function matlabbatch = setBatchComputeVDM(matlabbatch, fmapType, refImage) % matlabbatch = setBatchComputeVDM(type) % % adapted from spmup get_FM_workflow.m (@ commit @@ -21,12 +21,6 @@ error('This type of field map is not handled: %s', fmapType); end - FM_template = fullfile( ... - spm('dir'), ... - 'toolbox', ... - 'FieldMap', ... - 'T1.nii'); - UF = struct( ... 'method', 'Mark3D', ... 'fwhm', 10, ... @@ -34,7 +28,6 @@ 'ws', 1); MF = struct( ... - 'template', {FM_template}, ... 'fwhm', 5, ... 'nerode', 2, ... 'ndilate', 4, ... @@ -51,9 +44,18 @@ 'uflags', UF, ... 'mflags', MF); + fieldMapTemplate = spm_select( ... + 'FPList', ... + fullfile( ... + spm('dir'), ... + 'toolbox', ... + 'FieldMap'), ... + '^T1.nii$'); + defaultsval.mflags.template = { fieldMapTemplate }; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).defaults.defaultsval = defaultsval; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).session.epi = ''; % value needed + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).session.epi = {refImage}; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchvdm = 1; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).sessname = 'run'; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).writeunwarped = 1; diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m index 3a0dc45b..5aa07eed 100644 --- a/src/batches/setBatchCoregistrationFmap.m +++ b/src/batches/setBatchCoregistrationFmap.m @@ -6,8 +6,7 @@ % assumes all the fieldmap relate to the current task % - use the "for" metadata field % - implement for 'phase12', 'fieldmap', 'epi' - - + fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); % Create rough mean of the 1rst run to improve SNR for coregistration @@ -45,7 +44,7 @@ otherImages{1} = strrep(fmapFiles{1}, 'phasediff', 'magnitude2'); otherImages{2} = fmapFiles{1}; - matlabbatch = setBatchCoregistrationGeneral(matlabbatch, refImage, srcImage, otherImages); + matlabbatch = setBatchCoregistration(matlabbatch, refImage, srcImage, otherImages); end diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m index cad552df..1b064bb4 100644 --- a/src/batches/setBatchCreateVDMs.m +++ b/src/batches/setBatchCreateVDMs.m @@ -7,8 +7,14 @@ % - use the "for" metadata field % - implement for 'phase12', 'fieldmap', 'epi' + fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); + [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1}); + [fileName, subFuncDataDir] = getBoldFilename(BIDS, subID, sessions{1}, runs{1}, opt); + refImage = validationInputFile(subFuncDataDir, fileName, 'mean_'); + matlabbatch = []; for iSes = 1:nbSessions @@ -19,7 +25,7 @@ for iRun = 1:numel(runs) - matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff'); + matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff', refImage); % TODO % - Move to getInfo @@ -49,6 +55,7 @@ matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID); + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.tert = totalReadoutTime; end diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index d5090ec7..ee5560ae 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -32,21 +32,13 @@ function bidsCreateVDM(opt) printProcessingSubject(groupName, iSub, subID); - fprintf(1, ' FIELDMAP WORKFLOW: COREGISTERING FIELD MAPS TO FIRST FUNC IMAGE\n'); - - matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); - % - % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); - - % spm_jobman('run', matlabbatch); - - fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); + % matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); + % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); + % spm_jobman('run', matlabbatch); matlabbatch = setBatchCreateVDMs(opt, BIDS, subID); - saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID); - - % spm_jobman('run', matlabbatch); + spm_jobman('run', matlabbatch); % TODO % delete temporary mean images ?? From 817ee3ce250ec6dd88563a50d706df94227e0475 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 15:28:04 +0100 Subject: [PATCH 22/40] add function for image calculation --- src/batches/setBatchImageCalculation.m | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/batches/setBatchImageCalculation.m diff --git a/src/batches/setBatchImageCalculation.m b/src/batches/setBatchImageCalculation.m new file mode 100644 index 00000000..e2368b00 --- /dev/null +++ b/src/batches/setBatchImageCalculation.m @@ -0,0 +1,16 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function matlabbatch = setBatchImageCalculation(matlabbatch, input, output, outDir, expression) + +matlabbatch{end+1}.spm.util.imcalc.input = input; +matlabbatch{end}.spm.util.imcalc.output = output; +matlabbatch{end}.spm.util.imcalc.outdir = { outDir }; +matlabbatch{end}.spm.util.imcalc.expression = expression; + +% matlabbatch{1}.spm.util.imcalc.var = struct('name', {}, 'value', {}); +% matlabbatch{1}.spm.util.imcalc.options.dmtx = 0; +% matlabbatch{1}.spm.util.imcalc.options.mask = 0; +% matlabbatch{1}.spm.util.imcalc.options.interp = 1; +% matlabbatch{1}.spm.util.imcalc.options.dtype = 4; + +end From e879516e0eb31fdaf4bde8e742f2046cca57a3f1 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 15:28:25 +0100 Subject: [PATCH 23/40] remove checkoption duplicate --- src/utils/checkOptions.m | 106 --------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 src/utils/checkOptions.m diff --git a/src/utils/checkOptions.m b/src/utils/checkOptions.m deleted file mode 100644 index 07f0e605..00000000 --- a/src/utils/checkOptions.m +++ /dev/null @@ -1,106 +0,0 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers - -function opt = checkOptions(opt) - % opt = checkOptions(opt) - % - % we check the option inputs and add any missing field with some defaults - - fieldsToSet = setDefaultOption(); - - opt = setDefaultFields(opt, fieldsToSet); - - if ~isfield(opt, 'taskName') || isempty(opt.taskName) - - errorStruct.identifier = 'checkOptions:noTask'; - errorStruct.message = sprintf( ... - 'Provide the name of the task to analyze.'); - error(errorStruct); - - end - - if ~all(cellfun(@ischar, opt.groups)) - - disp(opt.groups); - - errorStruct.identifier = 'checkOptions:groupNotString'; - errorStruct.message = sprintf( ... - 'All group names should be string.'); - error(errorStruct); - - end - - if ~isempty (opt.STC_referenceSlice) && length(opt.STC_referenceSlice) > 1 - - errorStruct.identifier = 'checkOptions:refSliceNotScalar'; - errorStruct.message = sprintf( ... - ['options.STC_referenceSlice should be a scalar.' ... - '\nCurrent value is: %d'], ... - opt.STC_referenceSlice); - error(errorStruct); - - end - - if ~isempty (opt.funcVoxelDims) && length(opt.funcVoxelDims) ~= 3 - - errorStruct.identifier = 'checkOptions:voxDim'; - errorStruct.message = sprintf( ... - ['opt.funcVoxelDims should be a vector of length 3. '... - '\nCurrent value is: %d'], ... - opt.funcVoxelDims); - error(errorStruct); - - end - - opt = orderfields(opt); - -end - -function fieldsToSet = setDefaultOption() - % this defines the missing fields - - % group of subjects to analyze - fieldsToSet.groups = {''}; - % suject to run in each group - fieldsToSet.subjects = {[]}; - fieldsToSet.zeropad = 2; - - % session number and type of the anatomical reference - fieldsToSet.anatReference.type = 'T1w'; - fieldsToSet.anatReference.session = 1; - - % space where we conduct the analysis - fieldsToSet.space = 'MNI'; - - % The directory where the raw and derivatives are located - fieldsToSet.dataDir = ''; - fieldsToSet.derivativesDir = ''; - - % Options for slice time correction - % If left unspecified the slice timing will be done using the mid-volume acquisition - % time point as reference. - % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) - % instead of the slice index of the reference slice (the "SPM" way of doing things). - % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing - fieldsToSet.STC_referenceSlice = []; % reference slice: middle acquired slice - fieldsToSet.sliceOrder = []; % To be used if SPM can't extract slice info - - % fieldsToSet for normalize - % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. - fieldsToSet.funcVoxelDims = []; - - % specify the model file that contains the contrasts to compute - fieldsToSet.contrastList = {}; - fieldsToSet.model.file = ''; - - % specify the results to compute - fieldsToSet.result.Steps = struct( ... - 'Level', '', ... % dataset, run, subject - 'Contrasts', struct( ... - 'Name', '', ... - 'Mask', false, ... - 'MC', 'FWE', ... % FWE, none, FDR - 'p', 0.05, ... - 'k', 0, ... - 'NIDM', true)); - -end From 70aa86b9e3099675f05b51883abafe46ab422773 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 18:00:09 +0100 Subject: [PATCH 24/40] update VDM pipeline --- demos/openneuro/ds000114_run.m | 1 - demos/openneuro/ds001168_run.m | 1 - src/batches/setBatchComputeVDM.m | 2 +- src/batches/setBatchCreateVDMs.m | 70 +++++++++++++++--------- src/batches/setBatchImageCalculation.m | 18 +++--- src/getBlipDirection.m | 23 ++++++++ src/getTotalReadoutTime.m | 30 ++++++++++ src/{ => utils}/printProcessingRun.m | 2 +- src/{ => utils}/printProcessingSubject.m | 2 +- src/workflows/bidsCreateVDM.m | 2 +- 10 files changed, 111 insertions(+), 40 deletions(-) create mode 100644 src/getBlipDirection.m create mode 100644 src/getTotalReadoutTime.m rename src/{ => utils}/printProcessingRun.m (89%) rename src/{ => utils}/printProcessingSubject.m (85%) diff --git a/demos/openneuro/ds000114_run.m b/demos/openneuro/ds000114_run.m index 1ddd6efc..c0fcbe27 100644 --- a/demos/openneuro/ds000114_run.m +++ b/demos/openneuro/ds000114_run.m @@ -4,7 +4,6 @@ clear; clc; -close all; % Smoothing to apply FWHM = 6; diff --git a/demos/openneuro/ds001168_run.m b/demos/openneuro/ds001168_run.m index b66b4271..b9756f5c 100644 --- a/demos/openneuro/ds001168_run.m +++ b/demos/openneuro/ds001168_run.m @@ -4,7 +4,6 @@ clear; clc; -close all; % Smoothing to apply FWHM = 6; diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m index 49bd1f14..efcf65b8 100644 --- a/src/batches/setBatchComputeVDM.m +++ b/src/batches/setBatchComputeVDM.m @@ -37,7 +37,7 @@ defaultsval = struct( ... 'et', [NaN NaN], ... 'maskbrain', 1, ... - 'blipdir', 1, ... % can be changed + 'blipdir', 1, ... 'tert', '', ... 'epifm', 0, ... % can be changed 'ajm', 0, ... diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m index 1b064bb4..00f56045 100644 --- a/src/batches/setBatchCreateVDMs.m +++ b/src/batches/setBatchCreateVDMs.m @@ -4,7 +4,6 @@ % TODO % assumes all the fieldmap relate to the current task - % - use the "for" metadata field % - implement for 'phase12', 'fieldmap', 'epi' fprintf(1, ' FIELDMAP WORKFLOW: CREATING VDMs \n'); @@ -28,7 +27,7 @@ matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff', refImage); % TODO - % - Move to getInfo + % Move to getInfo ? fmapFiles = spm_BIDS(BIDS, 'data', ... 'modality', 'fmap', ... 'sub', subID, ... @@ -37,25 +36,23 @@ phaseImage = fmapFiles{1}; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.phase = ... - {phaseImage}; + {phaseImage}; magnitudeImage = strrep(phaseImage, 'phasediff', 'magnitude1'); matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.magnitude = ... - {magnitudeImage}; + {magnitudeImage}; - metadata = spm_BIDS(BIDS, 'metadata', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessions{iSes}, ... - 'run', runs{iRun}); + [echotimes, totReadTime, blipDir, isEPI] = getFmapMetadata(BIDS, ... + subID, ... + sessions{iSes}, ... + runs{iRun}); - echotimes = 1000 * [ ... - metadata.EchoTime1, ... - metadata.EchoTime2]; % in milliseconds - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; + % totReadTime = 2; - totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID); - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.tert = totalReadoutTime; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.tert = totReadTime; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.blipdir = blipDir; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.epifm = isEPI; end @@ -63,12 +60,13 @@ end -function totalReadoutTime = getTotalReadoutTime(opt, BIDS, subID) +function varargout = getFmapMetadata(BIDS, subID, sessionID, runID) metadata = spm_BIDS(BIDS, 'metadata', ... - 'type', 'bold', ... + 'modality', 'fmap', ... 'sub', subID, ... - 'task', opt.taskName); + 'ses', sessionID, ... + 'run', runID); % func run metadata: if the fmap is applied to several % runs we take the metadata of the first run it must @@ -77,15 +75,37 @@ metadata = metadata{1}; end - if isfield(metadata, 'TotalReadoutTime') - totalReadoutTime = metadata.TotalReadoutTime; + echotimes = getEchoTimes(metadata); + + totalReadoutTime = getTotalReadoutTime(metadata); + + blipDir = getBlipDirection(metadata); + + isEPI = getFmapPulseSequenceType(metadata); + + varargout{1} = echotimes; + varargout{2} = totalReadoutTime; + varargout{3} = blipDir; + varargout{4} = isEPI; + +end + +function echotimes = getEchoTimes(metadata) + + echotimes = 1000 * [ ... + metadata.EchoTime1, ... + metadata.EchoTime2]; % in milliseconds + +end + +function isEPI = getFmapPulseSequenceType(metadata) + + isEPI = 0; - elseif isfield(metadata, 'RepetitionTime') - totalReadoutTime = metadata.RepetitionTime; + if isfield(metadata, 'PulseSequenceType') && ... + sum(strfind(metadata.PulseSequenceType, 'EPI')) ~= 0 - elseif isfield(metadata, 'EffectiveEchoSpacing') - totalReadoutTime = (metadata.NumberOfEchos - 1) * ... - metadata.EffectiveEchoSpacing; + isEPI = 1; end end diff --git a/src/batches/setBatchImageCalculation.m b/src/batches/setBatchImageCalculation.m index e2368b00..076af5e4 100644 --- a/src/batches/setBatchImageCalculation.m +++ b/src/batches/setBatchImageCalculation.m @@ -2,15 +2,15 @@ function matlabbatch = setBatchImageCalculation(matlabbatch, input, output, outDir, expression) -matlabbatch{end+1}.spm.util.imcalc.input = input; -matlabbatch{end}.spm.util.imcalc.output = output; -matlabbatch{end}.spm.util.imcalc.outdir = { outDir }; -matlabbatch{end}.spm.util.imcalc.expression = expression; + matlabbatch{end + 1}.spm.util.imcalc.input = input; + matlabbatch{end}.spm.util.imcalc.output = output; + matlabbatch{end}.spm.util.imcalc.outdir = { outDir }; + matlabbatch{end}.spm.util.imcalc.expression = expression; -% matlabbatch{1}.spm.util.imcalc.var = struct('name', {}, 'value', {}); -% matlabbatch{1}.spm.util.imcalc.options.dmtx = 0; -% matlabbatch{1}.spm.util.imcalc.options.mask = 0; -% matlabbatch{1}.spm.util.imcalc.options.interp = 1; -% matlabbatch{1}.spm.util.imcalc.options.dtype = 4; + % matlabbatch{1}.spm.util.imcalc.var = struct('name', {}, 'value', {}); + % matlabbatch{1}.spm.util.imcalc.options.dmtx = 0; + % matlabbatch{1}.spm.util.imcalc.options.mask = 0; + % matlabbatch{1}.spm.util.imcalc.options.interp = 1; + % matlabbatch{1}.spm.util.imcalc.options.dtype = 4; end diff --git a/src/getBlipDirection.m b/src/getBlipDirection.m new file mode 100644 index 00000000..d01802af --- /dev/null +++ b/src/getBlipDirection.m @@ -0,0 +1,23 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function blipDir = getBlipDirection(metadata) + + blipDir = 1; + + if isfield(metadata, 'PhaseEncodingDirection') && ... + ~isempty(metadata.PhaseEncodingDirection) + + switch metadata.PhaseEncodingDirection + + case {'i', 'j', 'y'} + blipDir = 1; + case {'i-', 'j-', 'y-'} + blipDir = -1; + otherwise + error('unknown phase encoding direction: %s', metadata.PhaseEncodingDirection); + + end + + end + +end diff --git a/src/getTotalReadoutTime.m b/src/getTotalReadoutTime.m new file mode 100644 index 00000000..615b3196 --- /dev/null +++ b/src/getTotalReadoutTime.m @@ -0,0 +1,30 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function totalReadoutTime = getTotalReadoutTime(metadata) + + % TODO + % double check this section + + totalReadoutTime = ''; + + % apparently this comes from the functional metadata ??? + if isfield(metadata, 'TotalReadoutTime') && ~isempty(metadata.TotalReadoutTime) + totalReadoutTime = metadata.TotalReadoutTime; + + % % from spmup: apparently this comes from the fmap metadata + % elseif isfield(metadata, 'PixelBandwidth') && ~isempty(metadata.PixelBandwidth) + % totalReadoutTime = 1 / fieldmap_param.PixelBandwidth * 1000; + % warning('PixelBandwidth is not a valid BIDS term.'); + % + % % apparently this comes from the functional metadata ??? + % elseif isfield(metadata, 'RepetitionTime') && ~isempty(metadata.RepetitionTime) + % totalReadoutTime = metadata.RepetitionTime; + % + % % apparently this comes from the functional metadata ??? + % elseif isfield(metadata, 'EffectiveEchoSpacing') && ~isempty(metadata.NumberOfEchos) + % totalReadoutTime = (metadata.NumberOfEchos - 1) * ... + % metadata.EffectiveEchoSpacing; + + end + +end diff --git a/src/printProcessingRun.m b/src/utils/printProcessingRun.m similarity index 89% rename from src/printProcessingRun.m rename to src/utils/printProcessingRun.m index dbab8a53..d0b3efba 100644 --- a/src/printProcessingRun.m +++ b/src/utils/printProcessingRun.m @@ -4,7 +4,7 @@ function printProcessingRun(groupName, iSub, subID, iSes, iRun) fprintf(1, ... [ ... - 'PROCESSING GROUP: %s' ... + ' PROCESSING GROUP: %s' ... 'SUBJECT No.: %i ' ... 'SUBJECT ID : %s ' ... 'SESSION: %i ' ... diff --git a/src/printProcessingSubject.m b/src/utils/printProcessingSubject.m similarity index 85% rename from src/printProcessingSubject.m rename to src/utils/printProcessingSubject.m index 308ad32a..61e4a00a 100644 --- a/src/printProcessingSubject.m +++ b/src/utils/printProcessingSubject.m @@ -3,7 +3,7 @@ function printProcessingSubject(groupName, iSub, subID) fprintf(1, [ ... - 'PROCESSING GROUP: %s' ... + ' PROCESSING GROUP: %s' ... 'SUBJECT No.: %i ' ... 'SUBJECT ID : %s \n'], ... groupName, iSub, subID); diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index ee5560ae..2ddbc593 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -32,7 +32,7 @@ function bidsCreateVDM(opt) printProcessingSubject(groupName, iSub, subID); - % matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); + matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); % spm_jobman('run', matlabbatch); From 519e99c7eeaf634320591ba0cf0e8fd3e042b1bc Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 19:16:26 +0100 Subject: [PATCH 25/40] fix reaLign --- src/batches/setBatchRealign.m | 9 +++++++-- ...ation.m => test_setBatchCoregistrationFuncToAnat.m} | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) rename tests/{test_setBatchCoregistration.m => test_setBatchCoregistrationFuncToAnat.m} (91%) diff --git a/src/batches/setBatchRealign.m b/src/batches/setBatchRealign.m index fbe62787..62772595 100644 --- a/src/batches/setBatchRealign.m +++ b/src/batches/setBatchRealign.m @@ -22,9 +22,14 @@ % :returns: - :matlabbatch: (structure) (dimension) % - :voxDim: (array) (dimension) - [matlabbatch, BIDS, subID, opt, action] = deal(varargin{:}); + if numel(varargin) < 5 + [matlabbatch, BIDS, subID, opt] = deal(varargin{:}); + action = ''; + else + [matlabbatch, BIDS, subID, opt, action] = deal(varargin{:}); + end - if nargin < 5 || isempty(action) + if isempty(action) action = 'realign'; end diff --git a/tests/test_setBatchCoregistration.m b/tests/test_setBatchCoregistrationFuncToAnat.m similarity index 91% rename from tests/test_setBatchCoregistration.m rename to tests/test_setBatchCoregistrationFuncToAnat.m index 8bdf6f4a..138029a1 100644 --- a/tests/test_setBatchCoregistration.m +++ b/tests/test_setBatchCoregistrationFuncToAnat.m @@ -1,4 +1,4 @@ -function test_suite = test_setBatchCoregistration %#ok<*STOUT> +function test_suite = test_setBatchCoregistrationFuncToAnat %#ok<*STOUT> try % assignment of 'localfunctions' is necessary in Matlab >= 2016 test_functions = localfunctions(); %#ok<*NASGU> catch % no problem; early Matlab versions can use initTestSuite fine @@ -6,7 +6,7 @@ initTestSuite; end -function test_setBatchCoregistrationBasic() +function test_setBatchCoregistrationFuncToAnatBasic() % necessarry to deal with SPM module dependencies spm_jobman('initcfg'); @@ -24,7 +24,7 @@ function test_setBatchCoregistrationBasic() opt.orderBatches.realign = 2; matlabbatch = {}; - matlabbatch = setBatchCoregistration(matlabbatch, BIDS, subID, opt); + matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt); nbRuns = 4; @@ -44,7 +44,7 @@ function test_setBatchCoregistrationBasic() end -function test_setBatchCoregistrationNative() +function test_setBatchCoregistrationFuncToAnatNative() % necessarry to deal with SPM module dependencies spm_jobman('initcfg'); @@ -63,7 +63,7 @@ function test_setBatchCoregistrationNative() opt.orderBatches.realign = 2; matlabbatch = {}; - matlabbatch = setBatchCoregistration(matlabbatch, BIDS, subID, opt); + matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt); nbRuns = 4; From 93754c45209e58013c510c96441353139125906e Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Fri, 30 Oct 2020 22:55:47 +0100 Subject: [PATCH 26/40] move templates --- {src/templates => templates}/templateFunction.m | 0 {src/templates => templates}/templateFunctionExample.m | 0 {src/templates => templates}/templateFunctionNumpy.m | 0 {src/templates => templates}/templateGetOption.m | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {src/templates => templates}/templateFunction.m (100%) rename {src/templates => templates}/templateFunctionExample.m (100%) rename {src/templates => templates}/templateFunctionNumpy.m (100%) rename {src/templates => templates}/templateGetOption.m (100%) diff --git a/src/templates/templateFunction.m b/templates/templateFunction.m similarity index 100% rename from src/templates/templateFunction.m rename to templates/templateFunction.m diff --git a/src/templates/templateFunctionExample.m b/templates/templateFunctionExample.m similarity index 100% rename from src/templates/templateFunctionExample.m rename to templates/templateFunctionExample.m diff --git a/src/templates/templateFunctionNumpy.m b/templates/templateFunctionNumpy.m similarity index 100% rename from src/templates/templateFunctionNumpy.m rename to templates/templateFunctionNumpy.m diff --git a/src/templates/templateGetOption.m b/templates/templateGetOption.m similarity index 100% rename from src/templates/templateGetOption.m rename to templates/templateGetOption.m From 627891f636d4e4ecc18f1fe7873f5ff31d413c74 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 10:02:17 +0100 Subject: [PATCH 27/40] make VDM wrokflow fetch metadata of associated func file --- src/batches/setBatchComputeVDM.m | 2 +- src/batches/setBatchCreateVDMs.m | 108 ++++++++++++++++++++++--------- src/getBlipDirection.m | 14 ++++ src/getTotalReadoutTime.m | 44 +++++++++++-- src/workflows/bidsCreateVDM.m | 4 +- 5 files changed, 133 insertions(+), 39 deletions(-) diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m index efcf65b8..2c840c1b 100644 --- a/src/batches/setBatchComputeVDM.m +++ b/src/batches/setBatchComputeVDM.m @@ -57,7 +57,7 @@ matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).session.epi = {refImage}; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchvdm = 1; - matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).sessname = 'run'; + matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).sessname = 'vdm-'; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).writeunwarped = 1; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).anat = ''; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchanat = 0; diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m index 00f56045..1f877b3e 100644 --- a/src/batches/setBatchCreateVDMs.m +++ b/src/batches/setBatchCreateVDMs.m @@ -42,12 +42,10 @@ matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.data.presubphasemag.magnitude = ... {magnitudeImage}; - [echotimes, totReadTime, blipDir, isEPI] = getFmapMetadata(BIDS, ... - subID, ... - sessions{iSes}, ... - runs{iRun}); - - % totReadTime = 2; + [echotimes, isEPI, totReadTime, blipDir] = getMetadataForVDM(BIDS, ... + subID, ... + sessions{iSes}, ... + runs{iRun}); matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.et = echotimes; matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.tert = totReadTime; @@ -60,52 +58,98 @@ end -function varargout = getFmapMetadata(BIDS, subID, sessionID, runID) - - metadata = spm_BIDS(BIDS, 'metadata', ... - 'modality', 'fmap', ... - 'sub', subID, ... - 'ses', sessionID, ... - 'run', runID); +function varargout = getMetadataForVDM(BIDS, subID, sessionID, runID) - % func run metadata: if the fmap is applied to several - % runs we take the metadata of the first run it must - % applied to - if numel(metadata) > 1 - metadata = metadata{1}; + % get metadata fmap and its associated func files + fmapMetadata = spm_BIDS(BIDS, 'metadata', ... + 'modality', 'fmap', ... + 'sub', subID, ... + 'ses', sessionID, ... + 'run', runID); + if numel(fmapMetadata) > 1 + fmapMetadata = fmapMetadata{1}; end - echotimes = getEchoTimes(metadata); + echotimes = getEchoTimes(fmapMetadata); - totalReadoutTime = getTotalReadoutTime(metadata); + isEPI = checkFmapPulseSequenceType(fmapMetadata); - blipDir = getBlipDirection(metadata); + varargout{1} = echotimes; + varargout{2} = isEPI; - isEPI = getFmapPulseSequenceType(metadata); + [totalReadoutTime, blipDir] = getMetadataFromIntendedForFunc(BIDS, fmapMetadata); - varargout{1} = echotimes; - varargout{2} = totalReadoutTime; - varargout{3} = blipDir; - varargout{4} = isEPI; + varargout{3} = totalReadoutTime; + varargout{4} = blipDir; end -function echotimes = getEchoTimes(metadata) +function echotimes = getEchoTimes(fmapMetadata) echotimes = 1000 * [ ... - metadata.EchoTime1, ... - metadata.EchoTime2]; % in milliseconds + fmapMetadata.EchoTime1, ... + fmapMetadata.EchoTime2]; % in milliseconds end -function isEPI = getFmapPulseSequenceType(metadata) +function isEPI = checkFmapPulseSequenceType(fmapMetadata) isEPI = 0; - if isfield(metadata, 'PulseSequenceType') && ... - sum(strfind(metadata.PulseSequenceType, 'EPI')) ~= 0 + if isfield(fmapMetadata, 'PulseSequenceType') && ... + sum(strfind(fmapMetadata.PulseSequenceType, 'EPI')) ~= 0 isEPI = 1; end end + +function [totalReadoutTime, blipDir] = getMetadataFromIntendedForFunc(BIDS, fmapMetadata) + % get metadata of the associated bold file + % find bold file this fmap is intended for, parse its filename and get its + % metadata + + % At the moment the VDM is created based on the characteristics of the last + % func file in the IntendedFor field + + % TODO + % - if there are several func file for this fmap and they have different + % characteristic this may require creating a VDM for each + + for iFile = 1:size(fmapMetadata.IntendedFor) + + if iscell(fmapMetadata.IntendedFor) + filename = fmapMetadata.IntendedFor{iFile}; + else + filename = fmapMetadata.IntendedFor(iFile, :); + end + filename = spm_file(filename, 'filename'); + + fragments = bids.internal.parse_filename(filename); + + funcMetadata = spm_BIDS(BIDS, 'metadata', ... + 'modality', 'func', ... + 'type', fragments.type, ... + 'sub', fragments.sub, ... + 'ses', fragments.ses, ... + 'run', fragments.run, ... + 'acq', fragments.acq); + + end + + totalReadoutTime = getTotalReadoutTime(funcMetadata); + + % temporary for designing + % totalReadoutTime = 63; + + if isempty(totalReadoutTime) + errorStruct.identifier = 'getMetadataForVDM:emptyReadoutTime'; + errorStruct.message = [ ... + 'Voxel displacement map creation requires a non empty value' ... + 'for the TotalReadoutTime of the bold sequence they are matched to.']; + error(errorStruct); + end + + blipDir = getBlipDirection(funcMetadata); + +end diff --git a/src/getBlipDirection.m b/src/getBlipDirection.m index d01802af..0212f7c6 100644 --- a/src/getBlipDirection.m +++ b/src/getBlipDirection.m @@ -1,6 +1,20 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function blipDir = getBlipDirection(metadata) + % + % Gets the total read out time of a sequence. + % + % USAGE:: + % + % blipDir = getBlipDirection(metadata) + % + % :param metadata: image metadata + % :type metadata: strcuture + % + % :returns: - :blipDir: + % + % Used to create the voxel dsiplacement map (VDM) from the fieldmap + % blipDir = 1; diff --git a/src/getTotalReadoutTime.m b/src/getTotalReadoutTime.m index 615b3196..b4c7770b 100644 --- a/src/getTotalReadoutTime.m +++ b/src/getTotalReadoutTime.m @@ -1,17 +1,33 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function totalReadoutTime = getTotalReadoutTime(metadata) - - % TODO - % double check this section + % + % Gets the total read out time of a sequence. + % + % USAGE:: + % + % totalReadoutTime = getTotalReadoutTime(metadata) + % + % :param metadata: image metadata + % :type metadata: strcuture + % + % :returns: - :totalReadoutTime: (float) in millisecond + % + % Used to create the voxel dsiplacement map (VDM) from the fieldmap + % totalReadoutTime = ''; - % apparently this comes from the functional metadata ??? + % apparently this comes from the functional metadata to create the VDM if isfield(metadata, 'TotalReadoutTime') && ~isempty(metadata.TotalReadoutTime) totalReadoutTime = metadata.TotalReadoutTime; + % TODO + % double check this section + % this was in spmup but I don't remember where I got this from + % % % from spmup: apparently this comes from the fmap metadata + % % but PixelBandwidth is not is not a valid BIDS term % elseif isfield(metadata, 'PixelBandwidth') && ~isempty(metadata.PixelBandwidth) % totalReadoutTime = 1 / fieldmap_param.PixelBandwidth * 1000; % warning('PixelBandwidth is not a valid BIDS term.'); @@ -25,6 +41,26 @@ % totalReadoutTime = (metadata.NumberOfEchos - 1) * ... % metadata.EffectiveEchoSpacing; + %% Phase enconded lines (PELines) and ReadOutTime + + % PELines = ((BaseResolution * PartialFourier)/iPat) + ((iPat-1)/iPAT) * ReferenceLines) = + % ReadoutDuration = PELines * InterEchoSpacing + + % GRAPPA=iPAT4 ; Partial Fourrier=6/8 ; 48 sli ; TE=25ms ; Res=0.75 mm + % Bandwidth Per Pixel Phase Encode = 15.873 + + %% According to Robert Trampel + + % For distortion correction: ignore Partial Fourrier and references lines + % BaseResolution/iPAT = PELines + + % Effective echo spacing: 2 ways to calculate, should be the same + % 1/(Bandwidth Per Pixel Phase Encode * Reconstructed phase lines) --> 0.246 ms + % echo spacing (syngo) / iPAT + + % SPM Total readout time = 1/"Bandwidth Per Pixel Phase Encode", stored in + % DICOM tag (0019, 1028) --> 63 ms + end end diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index 2ddbc593..7aee9b8f 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -33,8 +33,8 @@ function bidsCreateVDM(opt) printProcessingSubject(groupName, iSub, subID); matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); - % saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); - % spm_jobman('run', matlabbatch); + saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); + spm_jobman('run', matlabbatch); matlabbatch = setBatchCreateVDMs(opt, BIDS, subID); saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID); From 31ed35825f2a1969224e2fb2b753a3187890eb51 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 10:07:52 +0100 Subject: [PATCH 28/40] create new subfolders --- src/{ => group_level}/getGrpLevelContrastToCompute.m | 0 src/{ => group_level}/getRFXdir.m | 0 src/{ => reports}/reportBIDS.m | 0 src/{ => subject_level}/concatBetaImgTmaps.m | 0 src/{ => subject_level}/convertOnsetTsvToMat.m | 0 src/{ => subject_level}/createAndReturnOnsetFile.m | 0 src/{ => subject_level}/deleteResidualImages.m | 0 src/{ => subject_level}/getBoldFilenameForFFX.m | 0 src/{ => subject_level}/getFFXdir.m | 0 src/{ => subject_level}/specifyContrasts.m | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename src/{ => group_level}/getGrpLevelContrastToCompute.m (100%) rename src/{ => group_level}/getRFXdir.m (100%) rename src/{ => reports}/reportBIDS.m (100%) rename src/{ => subject_level}/concatBetaImgTmaps.m (100%) rename src/{ => subject_level}/convertOnsetTsvToMat.m (100%) rename src/{ => subject_level}/createAndReturnOnsetFile.m (100%) rename src/{ => subject_level}/deleteResidualImages.m (100%) rename src/{ => subject_level}/getBoldFilenameForFFX.m (100%) rename src/{ => subject_level}/getFFXdir.m (100%) rename src/{ => subject_level}/specifyContrasts.m (100%) diff --git a/src/getGrpLevelContrastToCompute.m b/src/group_level/getGrpLevelContrastToCompute.m similarity index 100% rename from src/getGrpLevelContrastToCompute.m rename to src/group_level/getGrpLevelContrastToCompute.m diff --git a/src/getRFXdir.m b/src/group_level/getRFXdir.m similarity index 100% rename from src/getRFXdir.m rename to src/group_level/getRFXdir.m diff --git a/src/reportBIDS.m b/src/reports/reportBIDS.m similarity index 100% rename from src/reportBIDS.m rename to src/reports/reportBIDS.m diff --git a/src/concatBetaImgTmaps.m b/src/subject_level/concatBetaImgTmaps.m similarity index 100% rename from src/concatBetaImgTmaps.m rename to src/subject_level/concatBetaImgTmaps.m diff --git a/src/convertOnsetTsvToMat.m b/src/subject_level/convertOnsetTsvToMat.m similarity index 100% rename from src/convertOnsetTsvToMat.m rename to src/subject_level/convertOnsetTsvToMat.m diff --git a/src/createAndReturnOnsetFile.m b/src/subject_level/createAndReturnOnsetFile.m similarity index 100% rename from src/createAndReturnOnsetFile.m rename to src/subject_level/createAndReturnOnsetFile.m diff --git a/src/deleteResidualImages.m b/src/subject_level/deleteResidualImages.m similarity index 100% rename from src/deleteResidualImages.m rename to src/subject_level/deleteResidualImages.m diff --git a/src/getBoldFilenameForFFX.m b/src/subject_level/getBoldFilenameForFFX.m similarity index 100% rename from src/getBoldFilenameForFFX.m rename to src/subject_level/getBoldFilenameForFFX.m diff --git a/src/getFFXdir.m b/src/subject_level/getFFXdir.m similarity index 100% rename from src/getFFXdir.m rename to src/subject_level/getFFXdir.m diff --git a/src/specifyContrasts.m b/src/subject_level/specifyContrasts.m similarity index 100% rename from src/specifyContrasts.m rename to src/subject_level/specifyContrasts.m From 4ab0fa6b4496a4b27d1cc3d7fedc83a3269bb305 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 12:02:17 +0100 Subject: [PATCH 29/40] make realign an unwarp use the VDM files if they are present - move files in subfolder - reorganize order of some input arguments - update test for checkOptions - factor some functions out to facilitate testing them in the future --- src/batches/setBatchCoregistrationFmap.m | 2 +- src/batches/setBatchCreateVDMs.m | 68 +++++-------------- src/batches/setBatchRealign.m | 23 ++++--- src/defaults/checkOptions.m | 5 ++ src/{ => fieldmaps}/getBlipDirection.m | 0 .../getMetadataFromIntendedForFunc.m | 63 +++++++++++++++++ src/{ => fieldmaps}/getTotalReadoutTime.m | 0 src/fieldmaps/getVdmFile.m | 60 ++++++++++++++++ src/workflows/bidsCreateVDM.m | 4 +- tests/test_checkOptions.m | 2 + 10 files changed, 163 insertions(+), 64 deletions(-) rename src/{ => fieldmaps}/getBlipDirection.m (100%) create mode 100644 src/fieldmaps/getMetadataFromIntendedForFunc.m rename src/{ => fieldmaps}/getTotalReadoutTime.m (100%) create mode 100644 src/fieldmaps/getVdmFile.m diff --git a/src/batches/setBatchCoregistrationFmap.m b/src/batches/setBatchCoregistrationFmap.m index 5aa07eed..4e73a142 100644 --- a/src/batches/setBatchCoregistrationFmap.m +++ b/src/batches/setBatchCoregistrationFmap.m @@ -1,6 +1,6 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers -function matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID) +function matlabbatch = setBatchCoregistrationFmap(BIDS, opt, subID) % TODO % assumes all the fieldmap relate to the current task diff --git a/src/batches/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m index 1f877b3e..79d782df 100644 --- a/src/batches/setBatchCreateVDMs.m +++ b/src/batches/setBatchCreateVDMs.m @@ -1,7 +1,21 @@ % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers -function matlabbatch = setBatchCreateVDMs(opt, BIDS, subID) - +function matlabbatch = setBatchCreateVDMs(BIDS, opt, subID) + % + % Short description of what the function does goes here. + % + % USAGE:: + % + % matlabbatch = setBatchCreateVDMs(BIDS, opt, subID) + % + % :param BIDS: + % :type BIDS: structure + % :param opt: + % :type opt: structure + % :param subID: + % + % :returns: - :matlabbatch: (structure) (dimension) + % % TODO % assumes all the fieldmap relate to the current task % - implement for 'phase12', 'fieldmap', 'epi' @@ -103,53 +117,3 @@ end end - -function [totalReadoutTime, blipDir] = getMetadataFromIntendedForFunc(BIDS, fmapMetadata) - % get metadata of the associated bold file - % find bold file this fmap is intended for, parse its filename and get its - % metadata - - % At the moment the VDM is created based on the characteristics of the last - % func file in the IntendedFor field - - % TODO - % - if there are several func file for this fmap and they have different - % characteristic this may require creating a VDM for each - - for iFile = 1:size(fmapMetadata.IntendedFor) - - if iscell(fmapMetadata.IntendedFor) - filename = fmapMetadata.IntendedFor{iFile}; - else - filename = fmapMetadata.IntendedFor(iFile, :); - end - filename = spm_file(filename, 'filename'); - - fragments = bids.internal.parse_filename(filename); - - funcMetadata = spm_BIDS(BIDS, 'metadata', ... - 'modality', 'func', ... - 'type', fragments.type, ... - 'sub', fragments.sub, ... - 'ses', fragments.ses, ... - 'run', fragments.run, ... - 'acq', fragments.acq); - - end - - totalReadoutTime = getTotalReadoutTime(funcMetadata); - - % temporary for designing - % totalReadoutTime = 63; - - if isempty(totalReadoutTime) - errorStruct.identifier = 'getMetadataForVDM:emptyReadoutTime'; - errorStruct.message = [ ... - 'Voxel displacement map creation requires a non empty value' ... - 'for the TotalReadoutTime of the bold sequence they are matched to.']; - error(errorStruct); - end - - blipDir = getBlipDirection(funcMetadata); - -end diff --git a/src/batches/setBatchRealign.m b/src/batches/setBatchRealign.m index 62772595..7c9c5610 100644 --- a/src/batches/setBatchRealign.m +++ b/src/batches/setBatchRealign.m @@ -66,18 +66,18 @@ for iRun = 1:nbRuns % get the filename for this bold run for this task - [fileName, subFuncDataDir] = getBoldFilename( ... - BIDS, ... - subID, ... - sessions{iSes}, ... - runs{iRun}, ... - opt); + [boldFilename, subFuncDataDir] = getBoldFilename( ... + BIDS, ... + subID, ... + sessions{iSes}, ... + runs{iRun}, ... + opt); % check that the file with the right prefix exist and we get and % save its voxeldimension prefix = getPrefix('preprocess', opt); - file = validationInputFile(subFuncDataDir, fileName, prefix); - [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName); + file = validationInputFile(subFuncDataDir, boldFilename, prefix); + [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, boldFilename); if size(file, 1) > 1 errorStruct.identifier = 'setBatchRealign:tooManyFiles'; @@ -88,10 +88,15 @@ fprintf(1, ' %s\n', file); if strcmp(action, 'realignUnwarp') - matlabbatch{end}.spm.spatial.realignunwarp.data(1, runCounter).pmscan = ''; + + vdmFile = getVdmFile(BIDS, opt, boldFilename); + matlabbatch{end}.spm.spatial.realignunwarp.data(1, runCounter).pmscan = { vdmFile }; matlabbatch{end}.spm.spatial.realignunwarp.data(1, runCounter).scans = { file }; + else + matlabbatch{end}.spm.spatial.realign.estwrite.data{1, runCounter} = { file }; + end runCounter = runCounter + 1; diff --git a/src/defaults/checkOptions.m b/src/defaults/checkOptions.m index c840ad0c..952d34cd 100644 --- a/src/defaults/checkOptions.m +++ b/src/defaults/checkOptions.m @@ -88,6 +88,11 @@ fieldsToSet.STC_referenceSlice = []; % reference slice: middle acquired slice fieldsToSet.sliceOrder = []; % To be used if SPM can't extract slice info + % when opt.ignoreFieldmaps is set to false, the + % preprocessing pipeline will look for the voxel displacement maps (created by + % the corresponding workflow) and will use them for realign and unwarp + fieldsToSet.ignoreFieldmaps = false; + % fieldsToSet for normalize % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. fieldsToSet.funcVoxelDims = []; diff --git a/src/getBlipDirection.m b/src/fieldmaps/getBlipDirection.m similarity index 100% rename from src/getBlipDirection.m rename to src/fieldmaps/getBlipDirection.m diff --git a/src/fieldmaps/getMetadataFromIntendedForFunc.m b/src/fieldmaps/getMetadataFromIntendedForFunc.m new file mode 100644 index 00000000..e0b28830 --- /dev/null +++ b/src/fieldmaps/getMetadataFromIntendedForFunc.m @@ -0,0 +1,63 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function [totalReadoutTime, blipDir] = getMetadataFromIntendedForFunc(BIDS, fmapMetadata) + % get metadata of the associated bold file + % find bold file this fmap is intended for, parse its filename and get its + % metadata + % + % USAGE:: + % + % [totalReadoutTime, blipDir] = getMetadataFromIntendedForFunc(BIDS, fmapMetadata) + % + % :param BIDS: + % :type BIDS: structure + % :param fmapMetadata: + % :type fmapMetadata: structure + % + % :returns: - :totalReadoutTime: (type) (dimension) + % - :blipDir: (type) (dimension) + % + % At the moment the VDM is created based on the characteristics of the last + % func file in the IntendedFor field + % + % TODO + % - if there are several func file for this fmap and they have different + % characteristic this may require creating a VDM for each + + for iFile = 1:size(fmapMetadata.IntendedFor) + + if iscell(fmapMetadata.IntendedFor) + filename = fmapMetadata.IntendedFor{iFile}; + else + filename = fmapMetadata.IntendedFor(iFile, :); + end + filename = spm_file(filename, 'filename'); + + fragments = bids.internal.parse_filename(filename); + + funcMetadata = spm_BIDS(BIDS, 'metadata', ... + 'modality', 'func', ... + 'type', fragments.type, ... + 'sub', fragments.sub, ... + 'ses', fragments.ses, ... + 'run', fragments.run, ... + 'acq', fragments.acq); + + end + + totalReadoutTime = getTotalReadoutTime(funcMetadata); + + % temporary for designing + % totalReadoutTime = 63; + + if isempty(totalReadoutTime) + errorStruct.identifier = 'getMetadataForVDM:emptyReadoutTime'; + errorStruct.message = [ ... + 'Voxel displacement map creation requires a non empty value' ... + 'for the TotalReadoutTime of the bold sequence they are matched to.']; + error(errorStruct); + end + + blipDir = getBlipDirection(funcMetadata); + +end diff --git a/src/getTotalReadoutTime.m b/src/fieldmaps/getTotalReadoutTime.m similarity index 100% rename from src/getTotalReadoutTime.m rename to src/fieldmaps/getTotalReadoutTime.m diff --git a/src/fieldmaps/getVdmFile.m b/src/fieldmaps/getVdmFile.m new file mode 100644 index 00000000..6ba7a2df --- /dev/null +++ b/src/fieldmaps/getVdmFile.m @@ -0,0 +1,60 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function vdmFile = getVdmFile(BIDS, opt, boldFilename) + % + % returns the voxel displacement map associated with a given bold file + % + % USAGE:: + % + % [argout1, argout2] = templateFunction(argin1, [argin2 == default,] [argin3]) + % + % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, + % consectetur adipiscing elit. Ut congue nec est ac lacinia. + % :type argin1: type + % :param argin2: optional argument and its default value. And some of the + % options can be shown in litteral like ``this`` or ``that``. + % :type argin2: string + % :param argin3: (dimension) optional argument + % + % :returns: - :argout1: (type) (dimension) + % - :argout2: (type) (dimension) + + vdmFile = ''; + + fragments = bids.internal.parse_filename(boldFilename); + + if ~isfield(fragments, 'ses') + fragments.ses = ''; + end + + modalities = spm_BIDS(BIDS, 'modalities', ... + 'sub', fragments.sub, ... + 'ses', fragments.ses); + + if ~opt.ignoreFieldmaps && any(ismember('fmap', modalities)) + + fmapFiles = spm_BIDS(BIDS, 'data', ... + 'modality', 'fmap', ... + 'sub', fragments.sub, ... + 'ses', fragments.ses); + + fmapMetadata = spm_BIDS(BIDS, 'metadata', ... + 'modality', 'fmap', ... + 'sub', fragments.sub, ... + 'ses', fragments.ses); + + for iFile = 1:size(fmapFiles, 1) + + intendedFor = fmapMetadata{iFile}.IntendedFor; + + if strfind(intendedFor, boldFilename) %#ok + [pth, filename, ext] = spm_fileparts(fmapFiles{iFile}); + vdmFile = validationInputFile(pth, [filename ext], 'vdm5_sc'); + break + end + + end + + end + +end diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index 7aee9b8f..ef196688 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -32,11 +32,11 @@ function bidsCreateVDM(opt) printProcessingSubject(groupName, iSub, subID); - matlabbatch = setBatchCoregistrationFmap(opt, BIDS, subID); + matlabbatch = setBatchCoregistrationFmap(BIDS, opt, subID); saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID); spm_jobman('run', matlabbatch); - matlabbatch = setBatchCreateVDMs(opt, BIDS, subID); + matlabbatch = setBatchCreateVDMs(BIDS, opt, subID); saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID); spm_jobman('run', matlabbatch); diff --git a/tests/test_checkOptions.m b/tests/test_checkOptions.m index cbfe42b1..232f1684 100644 --- a/tests/test_checkOptions.m +++ b/tests/test_checkOptions.m @@ -94,6 +94,8 @@ function test_checkOptionsErrorVoxDim() expectedOptions.skullstrip.threshold = 0.75; + expectedOptions.ignoreFieldmaps = false; + expectedOptions.taskName = ''; expectedOptions.zeropad = 2; From 80637ba3cf2951a342bafb0a6e2053b0c2094ba4 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 12:25:43 +0100 Subject: [PATCH 30/40] update demo and try to run it in CI --- .travis.yml | 8 +++++++- demos/MoAE/MoAEpilot_run.m | 17 +++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index faa12678..3d40ea94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,4 +72,10 @@ jobs: before_script: - npm install `cat npm-requirements.txt` script: - - remark ./docs/ --frail \ No newline at end of file + - remark ./docs/ --frail + + - name: "run demo" + before_script: + - cd demos/MoAE + script: + - octave $OCTFLAGS --eval "MoAEpilot_run" \ No newline at end of file diff --git a/demos/MoAE/MoAEpilot_run.m b/demos/MoAE/MoAEpilot_run.m index 641747ba..074d500d 100644 --- a/demos/MoAE/MoAEpilot_run.m +++ b/demos/MoAE/MoAEpilot_run.m @@ -32,28 +32,25 @@ opt.space = 'individual'; %% Get data -% fprintf('%-40s:', 'Downloading dataset...'); -% urlwrite(URL, 'MoAEpilot.zip'); +fprintf('%-40s:', 'Downloading dataset...'); +urlwrite(URL, 'MoAEpilot.zip'); unzip('MoAEpilot.zip', fullfile(WD, 'output')); checkDependencies(); %% Run batches -% reportBIDS(opt); - -% bidsCopyRawFolder(opt, 1); +reportBIDS(opt); +bidsCopyRawFolder(opt, 1); bidsSegmentSkullStrip(opt); -return - bidsSTC(opt); bidsSpatialPrepro(opt); -anatomicalQA(opt); -bidsResliceTpmToFunc(opt); -functionalQA(opt); +%anatomicalQA(opt); +%bidsResliceTpmToFunc(opt); +%functionalQA(opt); bidsSmoothing(FWHM, opt); From 2296771a6d53c5625ef4084a3a1496cd09024223 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 12:28:34 +0100 Subject: [PATCH 31/40] mh fix --- demos/MoAE/MoAEpilot_run.m | 6 +++--- src/fieldmaps/getVdmFile.m | 27 +++++++++++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/demos/MoAE/MoAEpilot_run.m b/demos/MoAE/MoAEpilot_run.m index 074d500d..975a0632 100644 --- a/demos/MoAE/MoAEpilot_run.m +++ b/demos/MoAE/MoAEpilot_run.m @@ -48,9 +48,9 @@ bidsSpatialPrepro(opt); -%anatomicalQA(opt); -%bidsResliceTpmToFunc(opt); -%functionalQA(opt); +% anatomicalQA(opt); +% bidsResliceTpmToFunc(opt); +% functionalQA(opt); bidsSmoothing(FWHM, opt); diff --git a/src/fieldmaps/getVdmFile.m b/src/fieldmaps/getVdmFile.m index 6ba7a2df..8c57dfb0 100644 --- a/src/fieldmaps/getVdmFile.m +++ b/src/fieldmaps/getVdmFile.m @@ -6,18 +6,17 @@ % % USAGE:: % - % [argout1, argout2] = templateFunction(argin1, [argin2 == default,] [argin3]) + % vdmFile = getVdmFile(BIDS, opt, boldFilename) % - % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, - % consectetur adipiscing elit. Ut congue nec est ac lacinia. - % :type argin1: type - % :param argin2: optional argument and its default value. And some of the - % options can be shown in litteral like ``this`` or ``that``. - % :type argin2: string - % :param argin3: (dimension) optional argument + % :param BIDS: + % :type BIDS: structure + % :param opt: options + % :type opt: structure + % :param boldFilename: + % :type opt: string + % + % :returns: - :vdmFile: (string) % - % :returns: - :argout1: (type) (dimension) - % - :argout2: (type) (dimension) vdmFile = ''; @@ -32,6 +31,10 @@ 'ses', fragments.ses); if ~opt.ignoreFieldmaps && any(ismember('fmap', modalities)) + % We loop through the field maps and find the one that is intended for this + % bold file by reading from the metadata + % + % We break the loop when the file has been found fmapFiles = spm_BIDS(BIDS, 'data', ... 'modality', 'fmap', ... @@ -57,4 +60,8 @@ end + if isempty(vdmFile) + warning('No voxel displacement map associated with: \n %s', boldFilename); + end + end From 072fc030a2f4019440c4efecfa7000407038e85a Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 12:50:15 +0100 Subject: [PATCH 32/40] update README --- README.md | 1 + docs/README.md | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9c20804e..b109793c 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,4 @@ Thanks goes to these wonderful people This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! + diff --git a/docs/README.md b/docs/README.md index 93621c95..647e1a11 100644 --- a/docs/README.md +++ b/docs/README.md @@ -38,8 +38,8 @@ extensions = [ 'sphinx.ext.autodoc'] ``` -`matlab_src_dir` in `docs/source/conf.py` should have the path (relative to `conf.py`) -to the folder containing your matlab code: +`matlab_src_dir` in `docs/source/conf.py` should have the path (relative to +`conf.py`) to the folder containing your matlab code: ```python matlab_src_dir = os.path.dirname(os.path.abspath('../../src')) @@ -47,14 +47,16 @@ matlab_src_dir = os.path.dirname(os.path.abspath('../../src')) ## reStructured text markup -reStructured text mark up primers: +reStructured text mark up primers: -- on the [sphinx site](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) -- more [python oriented](https://pythonhosted.org/an_example_pypi_project/sphinx.html) +- on the + [sphinx site](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) +- more + [python oriented](https://pythonhosted.org/an_example_pypi_project/sphinx.html) -- typical doc strings templates - - [google way](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html) - - [numpy](https://www.sphinx-doc.org/en/master/usage/extensions/example_numpy.html#example-numpy) +- typical doc strings templates + - [google way](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html) + - [numpy](https://www.sphinx-doc.org/en/master/usage/extensions/example_numpy.html#example-numpy) ## "Templates" @@ -63,7 +65,7 @@ be done automatically. ```rst -.. automodule:: src .. <-- This is necessary for autodocumenting the rest +.. automodule:: src .. <-- This is necessary for autodocumenting the rest .. autofunction:: my_function @@ -92,4 +94,4 @@ website: https://readthedocs.org. You might need to be added as a maintainer of the doc. -The doc can be built from any branch of a repo. \ No newline at end of file +The doc can be built from any branch of a repo. From 91853089fac8e553f258462ae2010bfd3fa729de Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 12:56:48 +0100 Subject: [PATCH 33/40] fix test load and check options --- .travis.yml | 56 +++++++++++++++++--------------- tests/test_loadAndCheckOptions.m | 2 ++ 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3d40ea94..07529875 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,39 +24,42 @@ env: global: - OCTFLAGS="--no-gui --no-window-system --silent" +# TODO maybe this could be refactored as this is not needed to check the +# the markdown linting +before_install: + # install octave + - travis_retry sudo apt-get -y -qq update + - travis_retry sudo apt-get -y install octave + - travis_retry sudo apt-get -y install liboctave-dev + # install MOX unit + - git clone https://github.com/MOxUnit/MOxUnit.git --depth 1 + - cd MOxUnit + - make install + - cd .. + # install MOcov + - git clone https://github.com/MOcov/MOcov.git --depth 1 + - cd MOcov + - make install + - cd .. + # install SPM and the relevant patches for octave + - git clone https://github.com/spm/spm12.git --depth 1 + - make -C spm12/src PLATFORM=octave distclean + - make -C spm12/src PLATFORM=octave + - make -C spm12/src PLATFORM=octave install + # get data + - output_folder='demos/MoAE/output/' + - mkdir $output_folder + - curl http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip --output $output_folder'MoAEpilot.zip' + - unzip $output_folder'MoAEpilot.zip' -d $output_folder + jobs: include: # names the first job + - stage: "Test and linter" - name: "Unit tests and coverage" - before_install: - # install octave - - travis_retry sudo apt-get -y -qq update - - travis_retry sudo apt-get -y install octave - - travis_retry sudo apt-get -y install liboctave-dev - # install MOX unit - - git clone https://github.com/MOxUnit/MOxUnit.git --depth 1 - - cd MOxUnit - - make install - - cd .. - # install MOcov - - git clone https://github.com/MOcov/MOcov.git --depth 1 - - cd MOcov - - make install - - cd .. - # install SPM and the relevant patches for octave - - git clone https://github.com/spm/spm12.git --depth 1 - - make -C spm12/src PLATFORM=octave distclean - - make -C spm12/src PLATFORM=octave - - make -C spm12/src PLATFORM=octave install - # get data - - output_folder='demos/MoAE/output/' - - mkdir $output_folder - - curl http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip --output $output_folder'MoAEpilot.zip' - - unzip $output_folder'MoAEpilot.zip' -d $output_folder - script: - octave $OCTFLAGS --eval "runTests" - cat test_report.log | grep 0 @@ -74,6 +77,7 @@ jobs: script: - remark ./docs/ --frail + - stage: "Test pipeline" - name: "run demo" before_script: - cd demos/MoAE diff --git a/tests/test_loadAndCheckOptions.m b/tests/test_loadAndCheckOptions.m index 355fa9e5..ca8c4673 100644 --- a/tests/test_loadAndCheckOptions.m +++ b/tests/test_loadAndCheckOptions.m @@ -108,6 +108,8 @@ function test_loadAndCheckOptionsFromSeveralFiles() expectedOptions.skullstrip.threshold = 0.75; + expectedOptions.ignoreFieldmaps = false; + expectedOptions.taskName = ''; expectedOptions.zeropad = 2; From 7bc93f5837549084f0590600c1af88d0a6354870 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 13:02:48 +0100 Subject: [PATCH 34/40] update doc README --- docs/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index 647e1a11..c881e9fd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -49,8 +49,8 @@ matlab_src_dir = os.path.dirname(os.path.abspath('../../src')) reStructured text mark up primers: -- on the - [sphinx site](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) +- on the [sphinx + site](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html - more [python oriented](https://pythonhosted.org/an_example_pypi_project/sphinx.html) @@ -89,8 +89,8 @@ Add a [`.readthedocs.yml`](../.readthedocs.yml) file in the root of your repo. See [HERE](https://docs.readthedocs.io/en/stable/config-file/v2.html) for details. -You can then trigger the build of the doc by going to the [read the docs] -website: https://readthedocs.org. +You can then trigger the build of the doc by going to the +[read the docs website](https://readthedocs.org). You might need to be added as a maintainer of the doc. From 46f0ada9c8f8c62bade18cc52886b807c353f4d3 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 13:23:00 +0100 Subject: [PATCH 35/40] update demo ds001168 --- demos/openneuro/ds001168_getOption.m | 2 ++ demos/openneuro/ds001168_run.m | 18 +++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/demos/openneuro/ds001168_getOption.m b/demos/openneuro/ds001168_getOption.m index 3ef84b25..2f0ce7b8 100644 --- a/demos/openneuro/ds001168_getOption.m +++ b/demos/openneuro/ds001168_getOption.m @@ -20,6 +20,8 @@ opt.anatReference.type = 'T1w'; opt.anatReference.session = 1; + opt.ignoreFieldmaps = false; + %% DO NOT TOUCH opt = checkOptions(opt); % saveOptions(opt); diff --git a/demos/openneuro/ds001168_run.m b/demos/openneuro/ds001168_run.m index b9756f5c..305e1a10 100644 --- a/demos/openneuro/ds001168_run.m +++ b/demos/openneuro/ds001168_run.m @@ -28,19 +28,19 @@ %% Run batches -% reportBIDS(opt); +reportBIDS(opt); -% bidsCopyRawFolder(opt, 1); +bidsCopyRawFolder(opt, 1); bidsCreateVDM(opt); -% bidsSTC(opt); -% -% bidsSpatialPrepro(opt); -% -% anatomicalQA(opt); -% bidsResliceTpmToFunc(opt); -% functionalQA(opt); +bidsSTC(opt); + +bidsSpatialPrepro(opt); + +anatomicalQA(opt); +bidsResliceTpmToFunc(opt); +functionalQA(opt); % % bidsSmoothing(FWHM, opt); % From ab55c3b13142c35a5b497956185ecd2fd9407fca Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 13:25:18 +0100 Subject: [PATCH 36/40] update doc README --- docs/README.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index c881e9fd..335eab75 100644 --- a/docs/README.md +++ b/docs/README.md @@ -49,8 +49,8 @@ matlab_src_dir = os.path.dirname(os.path.abspath('../../src')) reStructured text mark up primers: -- on the [sphinx - site](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html +- on the [sphinx site](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) + - more [python oriented](https://pythonhosted.org/an_example_pypi_project/sphinx.html) @@ -60,18 +60,27 @@ reStructured text mark up primers: ## "Templates" +The templates to use for the doc are in the `templates` folder. + You then just need to insert this in your `.rst` file for the documentation to be done automatically. ```rst -.. automodule:: src .. <-- This is necessary for autodocumenting the rest +.. automodule:: src.folder_name .. <-- This is necessary for auto-documenting the rest + +.. autofunction:: function to document -.. autofunction:: my_function +``` + +To get the filenames of all the functions in a folder: -.. autofunction:: my_function_napoleon +``` bash +ls -l src/*.m | cut -c42- ``` +Increase the `42` to crop more characters. + ## Build the documentation locally From the `docs` directory: From cb6a317ac06bc404dd2e1d159cb4bce38ef83b47 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 13:45:04 +0100 Subject: [PATCH 37/40] fix CI --- .github/workflows/miss_hit.yml | 2 +- .travis.yml | 70 ++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/.github/workflows/miss_hit.yml b/.github/workflows/miss_hit.yml index d4cb7ae8..4603893a 100644 --- a/.github/workflows/miss_hit.yml +++ b/.github/workflows/miss_hit.yml @@ -28,7 +28,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip setuptools - pip3 install install miss_hit + pip3 install miss_hit - name: Miss_hit code quality run: | diff --git a/.travis.yml b/.travis.yml index 07529875..1f1cb5af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,34 +24,6 @@ env: global: - OCTFLAGS="--no-gui --no-window-system --silent" -# TODO maybe this could be refactored as this is not needed to check the -# the markdown linting -before_install: - # install octave - - travis_retry sudo apt-get -y -qq update - - travis_retry sudo apt-get -y install octave - - travis_retry sudo apt-get -y install liboctave-dev - # install MOX unit - - git clone https://github.com/MOxUnit/MOxUnit.git --depth 1 - - cd MOxUnit - - make install - - cd .. - # install MOcov - - git clone https://github.com/MOcov/MOcov.git --depth 1 - - cd MOcov - - make install - - cd .. - # install SPM and the relevant patches for octave - - git clone https://github.com/spm/spm12.git --depth 1 - - make -C spm12/src PLATFORM=octave distclean - - make -C spm12/src PLATFORM=octave - - make -C spm12/src PLATFORM=octave install - # get data - - output_folder='demos/MoAE/output/' - - mkdir $output_folder - - curl http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip --output $output_folder'MoAEpilot.zip' - - unzip $output_folder'MoAEpilot.zip' -d $output_folder - jobs: include: @@ -60,6 +32,34 @@ jobs: - stage: "Test and linter" - name: "Unit tests and coverage" + # TODO maybe this could be refactored as this is not needed to check the + # the markdown linting + install: + # install octave + - travis_retry sudo apt-get -y -qq update + - travis_retry sudo apt-get -y install octave + - travis_retry sudo apt-get -y install liboctave-dev + # install MOX unit + - git clone https://github.com/MOxUnit/MOxUnit.git --depth 1 + - cd MOxUnit + - make install + - cd .. + # install MOcov + - git clone https://github.com/MOcov/MOcov.git --depth 1 + - cd MOcov + - make install + - cd .. + # install SPM and the relevant patches for octave + - git clone https://github.com/spm/spm12.git --depth 1 + - make -C spm12/src PLATFORM=octave distclean + - make -C spm12/src PLATFORM=octave + - make -C spm12/src PLATFORM=octave install + # get data + - output_folder='demos/MoAE/output/' + - mkdir $output_folder + - curl http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip --output $output_folder'MoAEpilot.zip' + - unzip $output_folder'MoAEpilot.zip' -d $output_folder + script: - octave $OCTFLAGS --eval "runTests" - cat test_report.log | grep 0 @@ -79,6 +79,20 @@ jobs: - stage: "Test pipeline" - name: "run demo" + + # TODO maybe this could be refactored as this is not needed to check the + # the markdown linting + install: + # install octave + - travis_retry sudo apt-get -y -qq update + - travis_retry sudo apt-get -y install octave + - travis_retry sudo apt-get -y install liboctave-dev + # install SPM and the relevant patches for octave + - git clone https://github.com/spm/spm12.git --depth 1 + - make -C spm12/src PLATFORM=octave distclean + - make -C spm12/src PLATFORM=octave + - make -C spm12/src PLATFORM=octave install + before_script: - cd demos/MoAE script: From cc73a92f5849313bf9b4839663f7333fe8b37878 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 13:55:16 +0100 Subject: [PATCH 38/40] update doc --- docs/README.md | 8 +- docs/requirements.txt | 3 +- docs/source/batches.rst | 33 ++++--- docs/source/defaults.rst | 5 + docs/source/function_description.rst | 92 ++++++++++++++----- docs/source/index.rst | 5 +- docs/source/method_section_boilerplate.rst | 6 ++ docs/source/utils.rst | 22 ----- docs/source/workflows.rst | 6 +- .../templates}/templateFunction.m | 0 .../templates}/templateFunctionExample.m | 0 .../templates}/templateFunctionNumpy.m | 0 src/templates/templateGetOption.m | 71 ++++++++++++++ templates/templateGetOption.m | 52 ----------- 14 files changed, 182 insertions(+), 121 deletions(-) delete mode 100644 docs/source/utils.rst rename {templates => src/templates}/templateFunction.m (100%) rename {templates => src/templates}/templateFunctionExample.m (100%) rename {templates => src/templates}/templateFunctionNumpy.m (100%) create mode 100644 src/templates/templateGetOption.m delete mode 100644 templates/templateGetOption.m diff --git a/docs/README.md b/docs/README.md index 335eab75..24a540c3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -60,7 +60,7 @@ reStructured text mark up primers: ## "Templates" -The templates to use for the doc are in the `templates` folder. +The templates to use for the doc are in the `src/templates` folder. You then just need to insert this in your `.rst` file for the documentation to be done automatically. @@ -76,10 +76,12 @@ be done automatically. To get the filenames of all the functions in a folder: ``` bash -ls -l src/*.m | cut -c42- +ls -l src/*.m | cut -c42- | rev | cut -c 3- | rev ``` -Increase the `42` to crop more characters. +Increase the `42` to crop more characters at the beginning. + +Change the `3` to crop more characters at the end. ## Build the documentation locally diff --git a/docs/requirements.txt b/docs/requirements.txt index a4b9ccc8..42ea70da 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ Sphinx sphinxcontrib-matlabdomain sphinxcontrib-napoleon -sphinx_rtd_theme \ No newline at end of file +sphinx_rtd_theme +miss_hit \ No newline at end of file diff --git a/docs/source/batches.rst b/docs/source/batches.rst index 24cf0162..9da85ccb 100644 --- a/docs/source/batches.rst +++ b/docs/source/batches.rst @@ -7,18 +7,25 @@ List of functions to set SPM batches. .. automodule:: src.batches -.. autofunction:: setBatchCoregistration -.. autofunction:: setBatchNormalize -.. autofunction:: setBatchSegmentation +.. autofunction:: setBatchComputeVDM +.. autofunction:: setBatchCoregistrationFmap +.. autofunction:: setBatchCoregistrationFuncToAnat +.. autofunction:: setBatchCoregistration +.. autofunction:: setBatchCreateVDMs +.. autofunction:: setBatchFactorialDesign +.. autofunction:: setBatchImageCalculation +.. autofunction:: setBatchMeanAnatAndMask +.. autofunction:: setBatchNormalizationSpatialPrepro +.. autofunction:: setBatchNormalize +.. autofunction:: setBatchRealign +.. autofunction:: setBatchReslice +.. autofunction:: setBatchSaveCoregistrationMatrix +.. autofunction:: setBatchSegmentation +.. autofunction:: setBatchSelectAnat +.. autofunction:: setBatchSkullStripping +.. autofunction:: setBatchSmoothConImages +.. autofunction:: setBatchSmoothing .. autofunction:: setBatchSTC -.. autofunction:: setBatchFactorialDesign -.. autofunction:: setBatchRealign -.. autofunction:: setBatchSelectAnat .. autofunction:: setBatchSubjectLevelContrasts -.. autofunction:: setBatchMeanAnatAndMask -.. autofunction:: setBatchReslice -.. autofunction:: setBatchSmoothConImages -.. autofunction:: setBatchSubjectLevelGLMSpec -.. autofunction:: setBatchNormalizationSpatialPrepro -.. autofunction:: setBatchSaveCoregistrationMatrix -.. autofunction:: setBatchSmoothing \ No newline at end of file +.. autofunction:: setBatchSubjectLevelGLMSpec + \ No newline at end of file diff --git a/docs/source/defaults.rst b/docs/source/defaults.rst index 9956589a..85a1cc81 100644 --- a/docs/source/defaults.rst +++ b/docs/source/defaults.rst @@ -7,6 +7,10 @@ Defaults of the pipeline. .. automodule:: src.defaults +checkOptions +============ + +.. autofunction:: checkOptions spm_my_defaults =============== @@ -28,3 +32,4 @@ pipeline. .. autofunction:: spm_my_defaults + diff --git a/docs/source/function_description.rst b/docs/source/function_description.rst index 3ea62825..10745525 100644 --- a/docs/source/function_description.rst +++ b/docs/source/function_description.rst @@ -1,34 +1,76 @@ Function description -==================== +******************** List of functions in the ``src`` folder. ---- .. automodule:: src - -.. autofunction:: getBoldFilenameForFFX -.. autofunction:: getMeanFuncFilename -.. autofunction:: printProcessingRun -.. autofunction:: specifyContrasts -.. autofunction:: concatBetaImgTmaps -.. autofunction:: getBoldFilename -.. autofunction:: getPrefix -.. autofunction:: printProcessingSubject + +.. autofunction:: getAnatFilename +.. autofunction:: getBoldFilename +.. autofunction:: getData +.. autofunction:: getFuncVoxelDims +.. autofunction:: getInfo +.. autofunction:: getMeanFuncFilename +.. autofunction:: getPrefix +.. autofunction:: getRealignParamFile +.. autofunction:: getSliceOrder +.. autofunction:: getSpecificSubjects +.. autofunction:: setDerivativesDir .. autofunction:: unzipImgAndReturnsFullpathName -.. autofunction:: convertOnsetTsvToMat -.. autofunction:: getData -.. autofunction:: getRealignParamFile -.. autofunction:: createAndReturnOnsetFile -.. autofunction:: getFFXdir -.. autofunction:: getRFXdir -.. autofunction:: reportBIDS -.. autofunction:: getFuncVoxelDims -.. autofunction:: getSliceOrder -.. autofunction:: deleteResidualImages -.. autofunction:: getGrpLevelContrastToCompute -.. autofunction:: getSpecificSubjects + +Subject level model +=================== + +.. automodule:: src.subject_level + +.. autofunction:: concatBetaImgTmaps +.. autofunction:: convertOnsetTsvToMat +.. autofunction:: createAndReturnOnsetFile +.. autofunction:: deleteResidualImages +.. autofunction:: getBoldFilenameForFFX +.. autofunction:: getFFXdir +.. autofunction:: specifyContrasts + +Group level model +================= + +.. automodule:: src.group_level + +.. autofunction:: getGrpLevelContrastToCompute +.. autofunction:: getRFXdir + + +fieldmaps +========= + +.. automodule:: src.fieldmaps + +.. autofunction:: getBlipDirection +.. autofunction:: getMetadataFromIntendedForFunc +.. autofunction:: getTotalReadoutTime +.. autofunction:: getVdmFile + +Utilities +========= + +Utility functions + +---- + +.. automodule:: src.utils + +.. autofunction:: checkDependencies +.. autofunction:: createDataDictionary +.. autofunction:: getEnvInfo +.. autofunction:: getVersion +.. autofunction:: isOctave +.. autofunction:: loadAndCheckOptions +.. autofunction:: printCredits +.. autofunction:: printProcessingRun +.. autofunction:: printProcessingSubject .. autofunction:: saveMatlabBatch -.. autofunction:: getAnatFilename -.. autofunction:: getInfo -.. autofunction:: setDerivativesDir \ No newline at end of file +.. autofunction:: saveOptions +.. autofunction:: setDefaultFields +.. autofunction:: validationInputFile diff --git a/docs/source/index.rst b/docs/source/index.rst index a40529c8..aafb80c3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,8 +7,8 @@ Welcome to CPP SPM BIDS pipeline documentation! *********************************************** .. toctree:: - :maxdepth: 3 - :caption: Contentset_up + :maxdepth: 2 + :caption: Content set_up workflows @@ -18,7 +18,6 @@ Welcome to CPP SPM BIDS pipeline documentation! function_description method_section_boilerplate mancoreg - utils docker contributing diff --git a/docs/source/method_section_boilerplate.rst b/docs/source/method_section_boilerplate.rst index bf90cbe6..aacb8fbc 100644 --- a/docs/source/method_section_boilerplate.rst +++ b/docs/source/method_section_boilerplate.rst @@ -1,6 +1,12 @@ Boilerplate methods section *************************** +Use the report function to get a print out of the content of a dataset. + +.. automodule:: src.reports + +.. autofunction:: reportBIDS + Preprocessing ============= diff --git a/docs/source/utils.rst b/docs/source/utils.rst deleted file mode 100644 index 3c67325f..00000000 --- a/docs/source/utils.rst +++ /dev/null @@ -1,22 +0,0 @@ -Utilities -========= - -Utilities functions - ----- - -.. automodule:: src.utils - -.. autofunction:: checkDependencies -.. autofunction:: createDataDictionary -.. autofunction:: getVersion -.. autofunction:: loadAndCheckOptions -.. autofunction:: saveOptions -.. autofunction:: validationInputFile -.. autofunction:: checkOptions -.. autofunction:: getEnvInfo -.. autofunction:: isOctave -.. autofunction:: printCredits -.. autofunction:: setDefaultFields - - diff --git a/docs/source/workflows.rst b/docs/source/workflows.rst index 0f6e6367..21f5872c 100644 --- a/docs/source/workflows.rst +++ b/docs/source/workflows.rst @@ -116,5 +116,7 @@ Comput results --- .. autofunction:: bidsRealignReslice - -.. autofunction:: bidsRealignUnwarp \ No newline at end of file +.. autofunction:: bidsRealignUnwarp +.. autofunction:: bidsCreateVDM +.. autofunction:: bidsResliceTpmToFunc +.. autofunction:: bidsSegmentSkullStrip diff --git a/templates/templateFunction.m b/src/templates/templateFunction.m similarity index 100% rename from templates/templateFunction.m rename to src/templates/templateFunction.m diff --git a/templates/templateFunctionExample.m b/src/templates/templateFunctionExample.m similarity index 100% rename from templates/templateFunctionExample.m rename to src/templates/templateFunctionExample.m diff --git a/templates/templateFunctionNumpy.m b/src/templates/templateFunctionNumpy.m similarity index 100% rename from templates/templateFunctionNumpy.m rename to src/templates/templateFunctionNumpy.m diff --git a/src/templates/templateGetOption.m b/src/templates/templateGetOption.m new file mode 100644 index 00000000..b1eb5872 --- /dev/null +++ b/src/templates/templateGetOption.m @@ -0,0 +1,71 @@ +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers + +function opt = templateGetOption() + % returns a structure that contains the options chosen by the user to return + % the different workflows + + if nargin < 1 + opt = []; + end + + % group of subjects to analyze + % opt.groups = {''}; + % suject to run in each group + % opt.subjects = {[]}; + + % task to analyze + opt.taskName = 'balloonanalogrisktask'; + + % The directory where the data are located + opt.dataDir = '/home/remi/BIDS/ds001/rawdata'; + + % Where the data will go + % opt.derivativesDir = ''; + + % Options for slice time correction + % If left unspecified the slice timing will be done using the mid-volume acquisition + % time point as reference. + % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) + % instead of the slice index of the reference slice (the "SPM" way of doing things). + % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing + % opt.sliceOrder = []; + % opt.STC_referenceSlice = []; + + % when opt.ignoreFieldmaps is set to false, the + % preprocessing pipeline will look for the voxel displacement maps (created by + % the corresponding workflow) and will use them for realign and unwarp + % opt.ignoreFieldmaps = false; + + % session number and type of the anatomical reference + % opt.anatReference.type = 'T1w'; + % opt.anatReference.session = 1; + + % any voxel with p(grayMatter) + p(whiteMatter) + p(CSF) > threshold + % will be included in the skull stripping mask + % opt.skullstrip.threshold = 0.75; + + % space where we conduct the analysis + % opt.space = 'MNI'; + + % Options for normalize + % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. + % opt.funcVoxelDims = []; + + % specify the model file that contains the contrasts to compute + opt.model.univariate.file = ... + fullfile(fileparts(mfilename('fullpath')), ... + 'model', ... + 'model - balloonanalogriskUnivariate_smdl.json'); + + % specify the result to compute + % opt.result.Steps(1) = struct( ... + % 'Level', 'dataset', ... + % 'Contrasts', struct( ... + % 'Name', 'pumps_demean', ... + % 'Mask', false, ... + % 'MC', 'none', ... FWE, none, FDR + % 'p', 0.05, ... + % 'k', 0, ... + % 'NIDM', true)); + +end diff --git a/templates/templateGetOption.m b/templates/templateGetOption.m deleted file mode 100644 index a54ac336..00000000 --- a/templates/templateGetOption.m +++ /dev/null @@ -1,52 +0,0 @@ -% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers - -function opt = templateGetOption() - % returns a structure that contains the options chosen by the user to return - % the different workflows - - if nargin < 1 - opt = []; - end - - % group of subjects to analyze - opt.groups = {''}; - % suject to run in each group - opt.subjects = {[]}; - - % task to analyze - opt.taskName = 'balloonanalogrisktask'; - - % The directory where the data are located - opt.dataDir = '/home/remi/BIDS/ds001/rawdata'; - - % Options for slice time correction - % If left unspecified the slice timing will be done using the mid-volume acquisition - % time point as reference. - % Slice order must be entered in time unit (ms) (this is the BIDS way of doing things) - % instead of the slice index of the reference slice (the "SPM" way of doing things). - % More info here: https://en.wikibooks.org/wiki/SPM/Slice_Timing - opt.sliceOrder = []; - opt.STC_referenceSlice = []; - - % Options for normalize - % Voxel dimensions for resampling at normalization of functional data or leave empty [ ]. - opt.funcVoxelDims = []; - - % specify the model file that contains the contrasts to compute - opt.model.univariate.file = ... - fullfile(fileparts(mfilename('fullpath')), ... - 'model', ... - 'model - balloonanalogriskUnivariate_smdl.json'); - - % specify the result to compute - opt.result.Steps(1) = struct( ... - 'Level', 'dataset', ... - 'Contrasts', struct( ... - 'Name', 'pumps_demean', ... - 'Mask', false, ... - 'MC', 'none', ... FWE, none, FDR - 'p', 0.05, ... - 'k', 0, ... - 'NIDM', true)); - -end From 947f37b3c4f66fc0730e7e39795f60b7b0e14633 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 17:08:31 +0100 Subject: [PATCH 39/40] update travis CI update travis CI update travis CI update travis CI update travis CI update travis CI - pacify md linter update travis CI: fix octave path update travis CI fix markdown fix CI update travis CI update travis CI update travis CI update travis CI update travis CI --- .travis.yml | 98 +++++----- CONTRIBUTING.md | 336 +++++++++++++++++++++++++++++++++++ README.md | 27 ++- demos/MoAE/MoAEpilot_run.m | 11 +- demos/openneuro/openneuro.md | 10 +- docs/source/contributing.rst | 20 +-- runTests.m | 4 - tests/README.md | 2 +- 8 files changed, 430 insertions(+), 78 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f1cb5af..63e8b15f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,64 +2,74 @@ # This will only work on your repo if you have an account on travis and you # have set it up to run continuous integration on this this repo -# Linux distribution (bionic beaver) dist: bionic -# Language and version -language: python -python: - - "3.6" # current default Python on Travis CI +language: node_js +node_js: + - "10" cache: - apt: true # only works with Pro version + apt: true directories: - - node_modules # NPM packages for the remark markdown linter + - node_modules +# only run the CI for those branches branches: - only: # only run the CI for those branches - - master - - dev + only: + - master + - dev env: global: - OCTFLAGS="--no-gui --no-window-system --silent" -jobs: +# TODO maybe this could be refactored as this is not needed to check the +# the markdown linting +install: + # install octave + - travis_retry sudo apt-get -y -qq update + - travis_retry sudo apt-get -y install octave + - travis_retry sudo apt-get -y install liboctave-dev + + # install SPM and the relevant patches for octave + - git clone https://github.com/spm/spm12.git --depth 1 + - make -C spm12/src PLATFORM=octave distclean + - make -C spm12/src PLATFORM=octave + - make -C spm12/src PLATFORM=octave install + # update octave path + - octave $OCTFLAGS --eval "addpath(genpath(fullfile(pwd, 'lib'))); savepath();" + - octave $OCTFLAGS --eval "addpath(genpath(fullfile(pwd, 'src'))); savepath();" + - octave $OCTFLAGS --eval "addpath(fullfile(pwd, 'spm12')); savepath();" + +jobs: include: - # names the first job - - stage: "Test and linter" + #------------------------------------------------------------------------- + # first job + #------------------------------------------------------------------------- - name: "Unit tests and coverage" - # TODO maybe this could be refactored as this is not needed to check the - # the markdown linting - install: - # install octave - - travis_retry sudo apt-get -y -qq update - - travis_retry sudo apt-get -y install octave - - travis_retry sudo apt-get -y install liboctave-dev + before_script: + # install MOX unit - git clone https://github.com/MOxUnit/MOxUnit.git --depth 1 - cd MOxUnit - make install - cd .. + # install MOcov - git clone https://github.com/MOcov/MOcov.git --depth 1 - cd MOcov - make install - cd .. - # install SPM and the relevant patches for octave - - git clone https://github.com/spm/spm12.git --depth 1 - - make -C spm12/src PLATFORM=octave distclean - - make -C spm12/src PLATFORM=octave - - make -C spm12/src PLATFORM=octave install + # get data - output_folder='demos/MoAE/output/' - mkdir $output_folder - curl http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip --output $output_folder'MoAEpilot.zip' - unzip $output_folder'MoAEpilot.zip' -d $output_folder - + script: - octave $OCTFLAGS --eval "runTests" - cat test_report.log | grep 0 @@ -68,32 +78,28 @@ jobs: after_success: - bash <(curl -s https://codecov.io/bash) + #------------------------------------------------------------------------- + # second job + #------------------------------------------------------------------------- - name: "Check markdown" - language: node_js - node_js: - - "10" + before_script: + # install node.js dependencies - npm install `cat npm-requirements.txt` + script: + - remark *.md --frail + - remark ./demos/ --frail - remark ./docs/ --frail + - remark ./tests/ --frail - - stage: "Test pipeline" - - name: "run demo" - - # TODO maybe this could be refactored as this is not needed to check the - # the markdown linting - install: - # install octave - - travis_retry sudo apt-get -y -qq update - - travis_retry sudo apt-get -y install octave - - travis_retry sudo apt-get -y install liboctave-dev - # install SPM and the relevant patches for octave - - git clone https://github.com/spm/spm12.git --depth 1 - - make -C spm12/src PLATFORM=octave distclean - - make -C spm12/src PLATFORM=octave - - make -C spm12/src PLATFORM=octave install + #------------------------------------------------------------------------- + # third job + #------------------------------------------------------------------------- + - name: "Run demo" + + if: branch = master - before_script: - - cd demos/MoAE script: + - cd demos/MoAE - octave $OCTFLAGS --eval "MoAEpilot_run" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 345742f1..88b79e16 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,3 +8,339 @@ is partly created using the bash script `createDummyDataSet.sh`. ## Changelog + + diff --git a/README.md b/README.md index b109793c..8ee55558 100644 --- a/README.md +++ b/README.md @@ -32,18 +32,30 @@ This is a set of functions to fMRI analysis on a This can perform: - slice timing correction, + - spatial preprocessing: + - realignment, coregistration `func` to `anat`, `anat` segmentation, normalization to MNI space - - realignm and unwarp, coregistration `func` to `anat`, `anat` segmentation + + - realignm and unwarp, coregistration `func` to `anat`, `anat` + segmentation + - smoothing, + - Quality analysis: + - for anatomical data - for functional data + - GLM at the subject level -- GLM at the group level à la SPM (meaning using a summary statistics approach). -Please see our [documentation](https://cpp-bids-spm.readthedocs.io/en/latest/index.html) for more info. +- GLM at the group level à la SPM (meaning using a summary statistics + approach). + +Please see our +[documentation](https://cpp-bids-spm.readthedocs.io/en/latest/index.html) for +more info. The core functions are in the `src` folder. @@ -56,6 +68,8 @@ path. For instructions see the following links: + + | Dependencies | Used version | | ----------------------------------------------------------------------------------------- | ------------ | | [Matlab](https://www.mathworks.com/products/matlab.html) | 20??? | @@ -64,6 +78,7 @@ For instructions see the following links: | [Tools for NIfTI and ANALYZE image toolbox](https://github.com/sergivalverde/nifti_tools) | NA | | [spmup](https://github.com/CPernet/spmup) | NA | + ## Contributors @@ -71,8 +86,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - +
OliColli
OliColli

💻 🎨 📖
@@ -82,10 +96,9 @@ Thanks goes to these wonderful people
OliColli
OliColli

💻 🎨 📖
Remi Gau
Remi Gau

💻 📖 🚇 🎨 👀
- + This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! - diff --git a/demos/MoAE/MoAEpilot_run.m b/demos/MoAE/MoAEpilot_run.m index 975a0632..59e7c9b6 100644 --- a/demos/MoAE/MoAEpilot_run.m +++ b/demos/MoAE/MoAEpilot_run.m @@ -42,18 +42,21 @@ reportBIDS(opt); bidsCopyRawFolder(opt, 1); -bidsSegmentSkullStrip(opt); +% In case you just want to run segmentation and skull stripping +% bidsSegmentSkullStrip(opt); bidsSTC(opt); bidsSpatialPrepro(opt); +% The following do not run on octave for now (because of spmup) % anatomicalQA(opt); % bidsResliceTpmToFunc(opt); % functionalQA(opt); bidsSmoothing(FWHM, opt); -bidsFFX('specifyAndEstimate', opt, FWHM); -bidsFFX('contrasts', opt, FWHM); -bidsResults(opt, FWHM); +% The following crash on Travis CI +% bidsFFX('specifyAndEstimate', opt, FWHM); +% bidsFFX('contrasts', opt, FWHM); +% bidsResults(opt, FWHM); diff --git a/demos/openneuro/openneuro.md b/demos/openneuro/openneuro.md index 9f4c8cfe..c56643b6 100644 --- a/demos/openneuro/openneuro.md +++ b/demos/openneuro/openneuro.md @@ -1,13 +1,12 @@ - Download with datalad -In case you use the python package for datalad, activate your virtual environment (like conda). +In case you use the python package for datalad, activate your virtual +environment (like conda). ```bash conda activate datalad ``` - ## ds000114 Multi-subject and multi-session dataset. @@ -26,7 +25,7 @@ datalad get sub-0[12]/*/func/*linebisection* cd ds000001/ datalad get sub-0[12]/anat/*T1w* datalad get sub-0[12]/func/*balloonanalogrisktask* - ``` +``` ## ds001168 @@ -38,5 +37,4 @@ datalad get sub-0[12]/*/func/*linebisection* datalad get sub-0[12]/ses*/anat/* datalad get sub-0[12]/ses*/func/*fullbrain* - ``` - +``` diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index 1b2439b6..d9855ed0 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -17,26 +17,26 @@ Template proposal % (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [argout1, argout2] = templateFunction(argin1, argin2, argin3) - % - % Short description of what the function does goes here. - % - % USAGE: + % + % Short description of what the function does goes here. + % + % USAGE:: % % [argout1, argout2] = templateFunction(argin1, [argin2 == default,] [argin3]) % - % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, + % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, % consectetur adipiscing elit. Ut congue nec est ac lacinia. % :type argin1: type - % :param argin2: optional argument and its default value. And some of the + % :param argin2: optional argument and its default value. And some of the % options can be shown in litteral like ``this`` or ``that``. % :type argin2: string - % :param argin3: (dimension) optional argument + % :param argin3: (dimension) optional argument % - % :returns: - :argout1: (type) (dimension) - % - :argout2: (type) (dimension) + % :returns: - :argout1: (type) (dimension) + % - :argout2: (type) (dimension) % % .. todo: - % + % % - item 1 % - item 2 diff --git a/runTests.m b/runTests.m index ea84f508..ac531bf4 100644 --- a/runTests.m +++ b/runTests.m @@ -2,10 +2,6 @@ warning('OFF'); -addpath(fullfile(pwd, 'spm12')); -addpath(genpath(fullfile(pwd, 'src'))); -addpath(genpath(fullfile(pwd, 'lib'))); - spm('defaults', 'fMRI'); folderToCover = fullfile(pwd, 'src'); diff --git a/tests/README.md b/tests/README.md index f884994b..28db08aa 100644 --- a/tests/README.md +++ b/tests/README.md @@ -7,4 +7,4 @@ coverage = mocov( ... '-cover', fullfile(pwd, '..', 'subfun'), .... '-cover_xml_file', 'coverage.xml', ... '-cover_html_dir', 'coverage_html') -``` \ No newline at end of file +``` From e2610cd45fedc8078b58d6eebc28b6253cc3c27e Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 31 Oct 2020 17:08:49 +0100 Subject: [PATCH 40/40] update copyright dates --- src/getAnatFilename.m | 2 +- src/getData.m | 2 +- src/getFuncVoxelDims.m | 2 +- src/getInfo.m | 2 +- src/getMeanFuncFilename.m | 2 +- src/getPrefix.m | 2 +- src/getRealignParamFile.m | 2 +- src/getSliceOrder.m | 2 +- src/getSpecificSubjects.m | 2 +- src/setDerivativesDir.m | 2 +- src/subject_level/getBoldFilenameForFFX.m | 2 +- src/unzipImgAndReturnsFullpathName.m | 2 +- src/utils/setDefaultFields.m | 2 +- src/workflows/bidsRealignReslice.m | 2 +- src/workflows/bidsResliceTpmToFunc.m | 2 +- src/workflows/bidsSegmentSkullStrip.m | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/getAnatFilename.m b/src/getAnatFilename.m index 75a2305d..66a6cc9b 100644 --- a/src/getAnatFilename.m +++ b/src/getAnatFilename.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt) % [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt) diff --git a/src/getData.m b/src/getData.m index b1a0068c..f7656b37 100644 --- a/src/getData.m +++ b/src/getData.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [group, opt, BIDS] = getData(opt, BIDSdir, type) % [group, opt, BIDS] = getData(opt, BIDSdir, type) diff --git a/src/getFuncVoxelDims.m b/src/getFuncVoxelDims.m index c1192aad..d5fe3851 100644 --- a/src/getFuncVoxelDims.m +++ b/src/getFuncVoxelDims.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName) % [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName) diff --git a/src/getInfo.m b/src/getInfo.m index 877f6efd..dd4a8626 100644 --- a/src/getInfo.m +++ b/src/getInfo.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function varargout = getInfo(BIDS, subID, opt, info, varargin) % wrapper function to fetch specific info in a BIDS structure returned by diff --git a/src/getMeanFuncFilename.m b/src/getMeanFuncFilename.m index 8641a492..72a7459e 100644 --- a/src/getMeanFuncFilename.m +++ b/src/getMeanFuncFilename.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [meanImage, meanFuncDir] = getMeanFuncFilename(BIDS, subID, opt) % [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt) diff --git a/src/getPrefix.m b/src/getPrefix.m index 9f4776c0..2314b8d8 100644 --- a/src/getPrefix.m +++ b/src/getPrefix.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM) % [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM) diff --git a/src/getRealignParamFile.m b/src/getRealignParamFile.m index 7f743072..c4347b70 100644 --- a/src/getRealignParamFile.m +++ b/src/getRealignParamFile.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function realignParamFile = getRealignParamFile(fullpathBoldFileName, prefix) diff --git a/src/getSliceOrder.m b/src/getSliceOrder.m index 2c164165..37449801 100644 --- a/src/getSliceOrder.m +++ b/src/getSliceOrder.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function sliceOrder = getSliceOrder(opt, verbose) % get the slice order information from the BIDS data set or from getOption diff --git a/src/getSpecificSubjects.m b/src/getSpecificSubjects.m index 15d9f1c7..af259063 100644 --- a/src/getSpecificSubjects.m +++ b/src/getSpecificSubjects.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function group = getSpecificSubjects(opt, group, iGroup, subjects) diff --git a/src/setDerivativesDir.m b/src/setDerivativesDir.m index 6ee46fa3..3012c175 100644 --- a/src/setDerivativesDir.m +++ b/src/setDerivativesDir.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function opt = setDerivativesDir(opt) % derivativeDir sets the derivatives folder diff --git a/src/subject_level/getBoldFilenameForFFX.m b/src/subject_level/getBoldFilenameForFFX.m index a87aa925..d0c5b42a 100644 --- a/src/subject_level/getBoldFilenameForFFX.m +++ b/src/subject_level/getBoldFilenameForFFX.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function [boldFileName, prefix] = getBoldFilenameForFFX(varargin) % [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun) diff --git a/src/unzipImgAndReturnsFullpathName.m b/src/unzipImgAndReturnsFullpathName.m index 42116cd7..d13bed94 100644 --- a/src/unzipImgAndReturnsFullpathName.m +++ b/src/unzipImgAndReturnsFullpathName.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function unzippedFullpathImgName = unzipImgAndReturnsFullpathName(fullpathImgName) % unzippedFullpathImgName = unzipImgAndReturnsFullpathName(fullpathImgName) diff --git a/src/utils/setDefaultFields.m b/src/utils/setDefaultFields.m index 666ea7b6..98689d7d 100644 --- a/src/utils/setDefaultFields.m +++ b/src/utils/setDefaultFields.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function structure = setDefaultFields(structure, fieldsToSet) % structure = setDefaultFields(structure, fieldsToSet) diff --git a/src/workflows/bidsRealignReslice.m b/src/workflows/bidsRealignReslice.m index 3f64669a..06dc8444 100644 --- a/src/workflows/bidsRealignReslice.m +++ b/src/workflows/bidsRealignReslice.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function bidsRealignReslice(opt) % bidsRealignReslice(opt) diff --git a/src/workflows/bidsResliceTpmToFunc.m b/src/workflows/bidsResliceTpmToFunc.m index 91469abd..d5a5671f 100644 --- a/src/workflows/bidsResliceTpmToFunc.m +++ b/src/workflows/bidsResliceTpmToFunc.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 2020 CPP BIDS SPM-pipeline developpers function bidsResliceTpmToFunc(opt) % bidsResliceTpmToFunc(opt) diff --git a/src/workflows/bidsSegmentSkullStrip.m b/src/workflows/bidsSegmentSkullStrip.m index 5a60df9d..4ddbfe69 100644 --- a/src/workflows/bidsSegmentSkullStrip.m +++ b/src/workflows/bidsSegmentSkullStrip.m @@ -1,4 +1,4 @@ -% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers +% (C) Copyright 20120 CPP BIDS SPM-pipeline developpers function bidsSegmentSkullStrip(opt) %