diff --git a/.all-contributorsrc b/.all-contributorsrc
index e5082f64..22dbbab4 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2,6 +2,7 @@
"files": [
"README.md"
],
+ "contributorsSortAlphabetically": true,
"imageSize": 100,
"commit": false,
"contributors": [
diff --git a/.github/workflows/check_md_links.yml b/.github/workflows/check_md_links.yml
new file mode 100644
index 00000000..ac21eb73
--- /dev/null
+++ b/.github/workflows/check_md_links.yml
@@ -0,0 +1,18 @@
+name: Check Markdown links
+
+# checking for any dead links in markdown files
+
+on:
+ push:
+ branches:
+ - master
+ - dev
+ pull_request:
+ branches: '*'
+
+jobs:
+ markdown-link-check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - uses: gaurav-nelson/github-action-markdown-link-check@v1
diff --git a/.github/workflows/miss_hit.yml b/.github/workflows/miss_hit.yml
index 50c1df4d..4603893a 100644
--- a/.github/workflows/miss_hit.yml
+++ b/.github/workflows/miss_hit.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- master
+ - dev
pull_request:
branches: '*'
@@ -27,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: |
@@ -35,4 +36,4 @@ jobs:
- name: Miss_hit code style
run: |
- mh_style .
\ No newline at end of file
+ mh_style .
diff --git a/.gitignore b/.gitignore
index 8799dd29..8d7cf746 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,29 @@
*.log
*.html
*.zip
+*.ps
+options_task-*.json
+onsets*_events.mat
# files in the demo folder related to running the demo analysis
-demo/*.zip
-demo/output/*
+demos/MoAE/*.zip
+demos/MoAE/output/*
+demos/MoAE/derivatives/*
+
+# test folder
+tests/sub-01/*
+tests/group/*
+
+# ignore content of the build folder of the doc
+docs/build/*
+
+# ignore virtual env
+cpp_bids_spm/*
+
+# visual studio code stuff
+.vscode
+
+
+
+
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..b939c69b
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "lib/bids-matlab"]
+ path = lib/bids-matlab
+ url = https://github.com/cpp-lln-lab/bids-matlab.git
+[submodule "lib/spmup"]
+ path = lib/spmup
+ url = https://github.com/CPernet/spmup.git
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..6e0d1ce8
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,13 @@
+{
+ "parser": "markdown",
+ "proseWrap": "always",
+ "tabWidth": 2,
+ "overrides": [
+ {
+ "files": "*.md",
+ "options": {
+ "tabWidth": 4
+ }
+ }
+ ]
+}
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 00000000..6f095f08
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,26 @@
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ configuration: docs/source/conf.py
+ builder: html
+ fail_on_warning: true
+
+# Build documentation with MkDocs
+#mkdocs:
+# configuration: mkdocs.yml
+
+# Optionally build your docs in additional formats such as PDF
+formats:
+ - pdf
+
+# Optionally set the version of Python and requirements required to build your docs
+python:
+ version: 3.7
+ install:
+ - requirements: docs/requirements.txt
\ No newline at end of file
diff --git a/.remarkrc b/.remarkrc
new file mode 100644
index 00000000..201ce70e
--- /dev/null
+++ b/.remarkrc
@@ -0,0 +1,11 @@
+{
+ "plugins": [
+ "preset-lint-consistent",
+ "preset-lint-markdown-style-guide",
+ "preset-lint-recommended",
+ ["lint-no-duplicate-headings", false],
+ ["lint-list-item-indent", "tab-size"],
+ ["lint-maximum-line-length", true],
+ ["lint-maximum-heading-length", false]
+ ]
+}
diff --git a/.travis.yml b/.travis.yml
index 58ceeff9..63e8b15f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,47 +2,104 @@
# 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
+
+# only run the CI for those branches
+branches:
+ only:
+ - master
+ - dev
env:
global:
- OCTFLAGS="--no-gui --no-window-system --silent"
-before_install:
+# 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
-script:
- - octave $OCTFLAGS --eval "runTests"
- - cat test_report.log | grep 0
+ # 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:
+
+ #-------------------------------------------------------------------------
+ # first job
+ #-------------------------------------------------------------------------
+ - name: "Unit tests and coverage"
+
+ 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 ..
+
+ # 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
+
+ # to send the results to codecov to get our code coverage
+ after_success:
+ - bash <(curl -s https://codecov.io/bash)
+
+ #-------------------------------------------------------------------------
+ # second job
+ #-------------------------------------------------------------------------
+ - name: "Check markdown"
+
+ 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
-after_success:
- - bash <(curl -s https://codecov.io/bash)
+ #-------------------------------------------------------------------------
+ # third job
+ #-------------------------------------------------------------------------
+ - name: "Run demo"
+ if: branch = master
+ script:
+ - cd demos/MoAE
+ - octave $OCTFLAGS --eval "MoAEpilot_run"
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index e705c477..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,93 +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)*
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..88b79e16
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,346 @@
+# Contributing
+
+## Unit testing
+
+All tests are in the test folder. There is also an empty dummy BIDS dataset that
+is partly created using the bash script `createDummyDataSet.sh`.
+
+## Changelog
+
+
+
+
diff --git a/README.md b/README.md
index 6636e0a0..8ee55558 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
+
+
**Code quality and style**
[](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/actions)
-
+
**Unit tests and coverage**
@@ -13,330 +15,78 @@
[](https://doi.org/10.5281/zenodo.3556173)
**Contributors**
-
-[](#contributors)
-
-# Instructions for SPM12 Preprocessing Pipeline
-
-
+
-- [Instructions for SPM12 Preprocessing Pipeline](#instructions-for-spm12-preprocessing-pipeline)
- - [Dependencies](#dependencies)
- - [General description](#general-description)
- - [Assumption](#assumption)
- - [Setting up](#setting-up)
- - [getOptions](#getoptions)
- - [model JSON files](#model-json-files)
- - [Order of the analysis](#order-of-the-analysis)
- - [Docker](#docker)
- - [build docker image](#build-docker-image)
- - [run docker image](#run-docker-image)
- - [MRIQC](#mriqc)
- - [Details about some steps](#details-about-some-steps)
- - [Slice timing correction](#slice-timing-correction)
- - [Boiler plate methods section](#boiler-plate-methods-section)
- - [Preprocessing](#preprocessing)
- - [fMRI data analysis](#fmri-data-analysis)
- - [References](#references)
- - [Unit testing](#unit-testing)
- - [Changelog](#changelog)
- - [Contributors ✨](#contributors-)
+[](#contributors)
-
+
+
-## Dependencies
+# CPPL SPM12 Pipeline
-Make sure that the following toolboxes are installed and added to the matlab path.
+This is a set of functions to fMRI analysis on a
+[BIDS data set](https://bids.neuroimaging.io/) using SPM12.
-For instructions see the following links:
+This can perform:
-| Dependencies | Used version |
-|-------------------------------------------------------------------------------------------|--------------|
-| [Matlab](https://www.mathworks.com/products/matlab.html) | 20??? |
-| or [octave](https://www.gnu.org/software/octave/) | 4.? |
-| [SPM12](https://www.fil.ion.ucl.ac.uk/spm/software/spm12/) | v7487 |
-| [Tools for NIfTI and ANALYZE image toolbox](https://github.com/sergivalverde/nifti_tools) | NA |
-
-For simplicity the NIfTI tools toolbox has been added to this repo in the `subfun` folder.
-
-
-## General description
-
-This set of function will read and unzip the data from a [BIDS data set](https://bids.neuroimaging.io/). It will then perform:
- slice timing correction,
-- spatial preprocessing (realignment, normalization to MNI space),
-- smoothing,
-- GLM at the subject level and
-- GLM at the group level a la SPM (i.e summary statistics approach).
-
-This has to be run for each task independently. All parameters should preferably be changed in the `getOptions.m` file.
-
-It can also prepare the data to run an MVPA analysis by running a GLM for each subject on non-normalized images and get one beta image for each condition to be used in the MVPA.
-
-The core functions are in the sub-function folder `subfun`
-
-
-## Assumption
-
-At the moment this pipeline makes some assumptions:
-- it assumes that the dummy scans have been removed from the BIDS data set and it can jump straight into pre-processing,
-- it assumes the metadata for a given task are the same as those the first run of the first subject this pipeline is being run on,
-- it assumes that group are defined in the subject field (eg `sub-ctrl01`, `sub-blind01`, ...) and not in the `participants.tsv` file.
-
-
-## Setting up
-
-### getOptions
-
-
-All the details specific to your analysis should be set in the `getOptions.m`. There is a getOption_template file that shows you would set up the getOption file if one wanted to analyse the [ds001 data set from OpenNeuro](https://openneuro.org/datasets/ds000001/versions/57fecb0ccce88d000ac17538).
-
-Set the group of subjects to analyze.
-```
-opt.groups = {'control', 'blind'};
-```
-
-If there are no groups (i.e subjects names are of the form `sub-01` for example) or if you want to run all subjects of all groups then use:
-```matlab
-opt.groups = {''};
-opt.subjects = {[]};
-```
-
-If you have 2 groups (`cont` and `cat` for example) the following will run cont01, cont02, cat03, cat04..
-```matlab
-opt.groups = {'cont', 'cat'};
-opt.subjects = {[1 2], [3 4]};
-```
-If you have more than 2 groups but want to only run the subjects of 2 groups then you can use.
-```matlab
-opt.groups = {'cont', 'cat'};
-opt.subjects = {[], []};
-```
-You can also directly specify the subject label for the participants you want to run
-```matlab
-opt.groups = {''};
-opt.subjects = {'01', 'cont01', 'cat02', 'ctrl02', 'blind01'};
-```
-
-Set the task to analyze in the BIDS data set
-`opt.taskName = 'auditory'`
-
-The directory where your files are located on your computer: make sure you have a copy of the data set as this pipeline will change it.
-`opt.derivativesDir = '/Data/auditoryBIDS/derivatives'`
-
-Some more SPM options can be set in the `spm_my_defaults.m`.
-
-
-### model JSON files
-This files allow you to specify which contrasts to run and follow the BIDS statistical model extension and as implement by [fitlins](https://fitlins.readthedocs.io/en/latest/model.html)
-
-The model json file that describes:
-- out to prepare the regressors for the GLM: `Transformation`
-- the design matrix: `X`
-- the contrasts to compute: `contrasts`
-
-It also allows to specify those for different levels of the analysis:
-- run
-- session
-- subject
-- dataset
-
-An example of json file could look something like that.
-
-```json
-{
- "Name": "Basic",
- "Description": "",
- "Input": {
- "task": "motionloc"
- },
- "Steps": [
- {
- "Level": "subject",
- "AutoContrasts": ["stim_type.motion", "stim_type.static"],
- "Contrasts": [
- {
- "Name": "motion_vs_static",
- "ConditionList": [
- "stim_type.motion",
- "stim_type.static"
- ],
- "weights": [1, -1],
- "type": "t"
- }
- ]
- },
- {
- "Level": "dataset",
- "AutoContrasts": ["stim_type.motion", "stim_type.static", "motion_vs_static"]
- }
- ]
-}
-```
-
-In brief this means:
-- at the subject level automatically compute the t contrast against baseline for the condition `motion`and `static` and compute the t-contrats for motion VS static with these given weights.
-- at the level of the data set (so RFX) do the t contrast of the `motion`, `static`, `motion VS static`.
-
-We are currently using this to run different subject level GLM models for our univariate and multivariate analysis where in the first one we compute a con image that averages the beta image of all the runs where as in the latter case we get one con image for each run.
-
-
-## Order of the analysis
-
-1. __Remove Dummy Scans__:
-Unzip bold files and removes dummy scans by running the script (to be run even if `opt.numDummies` set to `0`): `BIDS_rmDummies.m`
-
-2. __Slice Time Correction__: Performs Slice Time Correction (STC) of the functional volumes by running the script: `BIDS_STC.m`
-
-STC will be performed using the information provided in the BIDS data set. It will use the mid-volume acquisition time point as as reference.
-
-The `getOption.m` fields related to STC can still be used to do some slice timing correction even no information is can be found in the BIDS data set.
-
-In general slice order and reference slice is 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 available on this page of the [SPM wikibook](https://en.wikibooks.org/wiki/SPM/Slice_Timing).
-
-3. __Spatial Preprocessing__:
-Performs spatial preprocessing by running the script: `BIDS_SpatialPrepro.m`
-
-4. __SMOOTHING__:
-Performs smoothing of the functional data by running the script: `BIDS_Smoothing.m`
-
-5. __FIXED EFFECTS ANALYSIS (FIRST-LEVEL ANALYSIS)__:
-Performs the fixed effects analysis by running the ffx script: `BIDS_FFX.m`
-
-This will run twice, once for model specification and another time for model estimation. See the function for more details.
-
-This will take each condition present in the `events.tsv` file of each run and convolve it with a canonical HRF. It will also add the 6 realignment parameters of every run as confound regressors.
-
-6. __RANDOM EFFECTS ANALYSIS (SECOND-LEVEL ANALYSIS)__:
-Performs the random effects analysis by running the RFX script: `BIDS_RFX.m`
-7. __GET THE RESULTS FROM A SPECIFIC CONTRAST__: `BIDS_Results.m`
+- spatial preprocessing:
-- See __"batch.m"__ for examples and for the order of the scripts.
-- See __"batch_dowload_run.m"__ for an example of how to download a data set and analyze it all in one go.
+ - realignment, coregistration `func` to `anat`, `anat` segmentation,
+ normalization to MNI space
+ - realignm and unwarp, coregistration `func` to `anat`, `anat`
+ segmentation
-## Docker
-
-The recipe to build the docker image is in the `Dockerfile`
-
-
-### build docker image
-
-To build the image with with octave and SPM the `Dockerfile` just type :
-
-`docker build -t cpp_spm:0.0.1 .`
-
-This will create an image with the tag name `cpp_spm_octave:0.0.1`
-
-
-### run docker image
-
-The following code would start the docker image and would map 2 folders one for `output` and one for `code` you want to run.
-
-``` bash
-docker run -it --rm \
--v [output_folder]:/output \
--v [code_folder]:/code cpp_spm:0.0.1
-```
-
-To test it you can copy the `batch_download_run.m` file in the `code` folder on your computer and then start running the docker and type:
-
-```bash
-cd /code # to change to the code folder inside the container (running the command 'ls' should show only batch_download_run.m )
-octave --no-gui --eval batch_download_run # to run the batch_download_run script
-```
-
-### MRIQC
-
-If you want to run some quality control on your data you can do it using MRIQC.
-
-```bash
-data_dir=pat_to_data # define where the data is
-
-docker run -it --rm -v $data_dir/raw:/data:ro -v $data_dir:/out poldracklab/mriqc:0.15.0 /data /out/derivatives/mriqc participant --verbose-reports --mem_gb 50 --n_procs 16 -m bold
-```
-
-## Details about some steps
-
-
-### Slice timing correction
-
-BELOW: some comments from [here](http://mindhive.mit.edu/node/109) on STC, when it should be applied
-
-_At what point in the processing stream should you use it?_
-
-_This is the great open question about slice timing, and it's not super-answerable. Both SPM and AFNI recommend you do it before doing realignment/motion correction, but it's not entirely clear why. The issue is this:_
-
-_If you do slice timing correction before realignment, you might look down your non-realigned time course for a given voxel on the border of gray matter and CSF, say, and see one TR where the head moved and the voxel sampled from CSF instead of gray. This would results in an interpolation error for that voxel, as it would attempt to interpolate part of that big giant signal into the previous voxel. On the other hand, if you do realignment before slice timing correction, you might shift a voxel or a set of voxels onto a different slice, and then you'd apply the wrong amount of slice timing correction to them when you corrected - you'd be shifting the signal as if it had come from slice 20, say, when it actually came from slice 19, and shouldn't be shifted as much._
-
-_There's no way to avoid all the error (short of doing a four-dimensional realignment process combining spatial and temporal correction - Remi's note: fMRIprep does it), but I believe the current thinking is that doing slice timing first minimizes your possible error. The set of voxels subject to such an interpolation error is small, and the interpolation into another TR will also be small and will only affect a few TRs in the time course. By contrast, if one realigns first, many voxels in a slice could be affected at once, and their whole time courses will be affected. I think that's why it makes sense to do slice timing first. That said, here's some articles from the SPM e-mail list that comment helpfully on this subject both ways, and there are even more if you do a search for "slice timing AND before" in the archives of the list._
-
-
-## Boiler plate methods section
-
-
-### Preprocessing
-
-The fMRI data were pre-processed and analyzed using statistical parametric mapping (SPM12 – v7487; Wellcome Center for Neuroimaging, London, UK; www.fil.ion.ucl.ac.uk/spm) running on {octave 4.{??} / matlab 20{XX} (Mathworks)}.
-
-The preprocessing of the functional images was performed in the following order: removing of dummy scans, {slice timing correction}, realignment, normalization to MNI, smoothing.
-
-{XX} dummy scans were removed to allow signal stabilization.
-
-{Slice timing correction was then performed taking the {XX} th slice as a reference (interpolation: sinc interpolation).}
-
-Functional scans from each participant were realigned using the mean image as a reference (SPM 2 passes ; number of degrees of freedom: 6 ; cost function: least square) (Friston et al, 1995).
-
-The mean image obtained from realignement was then co-registered to the anatomical T1 image (number of degrees of freedom: 6 ; cost function: normalized mutual information) (Friston et al, 1995). The transformation matrix from this coregistration was then applied to all the functional images.
-
-The anatomical T1 image was bias field corrected, segmented and normalized to MNI space (target space resolution: 1 mm ; interpolation: 4th degree b-spline) using a unified segmentation. The deformation field obtained from this step was then applied to all the functional images (target space resolution equal that used at acquisition ; interpolation: 4th degree b-spline)
-
-Functional MNI normalized images were then spatially smoothed using a 3D gaussian kernel (FWHM = {XX} mm).
-
-
-### fMRI data analysis
-
-At the subject level, we performed a mass univariate analysis with a linear regression at each voxel of the brain, using generalized least squares with a global FAST model to account for temporal auto-correlation (Corbin et al, 2018) and a drift fit with discrete cosine transform basis (128 seconds cut-off). Image intensity scaling was done run-wide before statistical modeling such that the mean image will have mean intracerebral intensity of 100.
-
-We modeled the fMRI experiment in an event related design with regressors entered into the run-specific design matrix after convolving the onsets of each event with a canonical hemodynamic response function (HRF).
-
-Table of conditions with duration of each event: WIP
+- smoothing,
-Nuisance covariates included the 6 realignment parameters to account for residual motion artefacts.
+- Quality analysis:
-Contrast images were computed for the following condition and spatially smoothed using a 3D gaussian kernel (FWHM = {XX} mm).
+ - for anatomical data
+ - for functional data
-Table of constrast with weight: WIP
+- GLM at the subject level
-Group level: WIP
+- 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.
-### References
+The core functions are in the `src` folder.
-Friston KJ, Ashburner J, Frith CD, Poline J-B, Heather JD & Frackowiak RSJ (1995) Spatial registration and normalization of images Hum. Brain Map. 2:165-189
+## Installation
-Corbin, N., Todd, N., Friston, K. J. & Callaghan, M. F. Accurate modeling of temporal correlations in rapidly sampled fMRI time series. Hum. Brain Mapp. 39, 3884–3897 (2018).
+### Dependencies
+Make sure that the following toolboxes are installed and added to the matlab
+path.
-## Unit testing
+For instructions see the following links:
-All tests are in the test folder. There is also an empty dummy BIDS dataset that is partly created using the bash script `createDummyDataSet.sh`.
+
-## Changelog
+| Dependencies | Used version |
+| ----------------------------------------------------------------------------------------- | ------------ |
+| [Matlab](https://www.mathworks.com/products/matlab.html) | 20??? |
+| or [octave](https://www.gnu.org/software/octave/) | 4.? |
+| [SPM12](https://www.fil.ion.ucl.ac.uk/spm/software/spm12/) | v7487 |
+| [Tools for NIfTI and ANALYZE image toolbox](https://github.com/sergivalverde/nifti_tools) | NA |
+| [spmup](https://github.com/CPernet/spmup) | NA |
-
+
-## Contributors ✨
+## Contributors
-Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+Thanks goes to these wonderful people
+([emoji key](https://allcontributors.org/docs/en/emoji-key)):
-
+
 OliColli 💻 🎨 📖 |
@@ -346,7 +96,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Remi Gau 💻 📖 🚇 🎨 👀 |
-
+
-This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+This project follows the
+[all-contributors](https://github.com/all-contributors/all-contributors)
+specification. Contributions of any kind welcome!
diff --git a/batch.m b/batch.m
deleted file mode 100644
index 68b2db57..00000000
--- a/batch.m
+++ /dev/null
@@ -1,62 +0,0 @@
-% __ ____ ____ _ _ _
-% / _)( _ \( _ \ | | / \ | )
-% ( (_ )___/ )___/ | |_ / _ \ | \
-% \__)(__) (__) |___||_/ \_||__)
-%
-% Thank you for using the CPP lap pipeline - version 0.0.3.
-%
-% Current list of contributors includes
-% Mohamed Rezk
-% Rémi Gau
-% Olivier Collignon
-% Ane Gurtubay
-% Marco Barilari
-%
-% Please cite using the following DOI:
-% https://doi.org/10.5281/zenodo.3554332
-%
-% For bug report, suggestions for improvements or contributions see our github repo:
-% https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline
-
-clear;
-clc;
-
-%% Run batches
-opt = getOption();
-
-% the cdirectory with this script becomes the current directory
-WD = pwd;
-
-% we add all the subfunctions that are in the sub directories
-addpath(genpath(WD));
-
-% In case some toolboxes need to be added the matlab path, specify and uncomment
-% in the lines below
-% toolbox_path = '';
-% addpath(fullfile(toolbox_path)
-
-checkDependencies();
-
-% copy raw folder into derivatives folder
-% BIDS_copyRawFolder(opt, 1)
-
-% preprocessing
-% BIDS_STC(opt);
-% BIDS_SpatialPrepro(opt);
-% BIDS_Smoothing(6, opt);
-
-% subject level Univariate
-% BIDS_FFX(1, 6, opt);
-% BIDS_FFX(2, 6, opt);
-
-% group level univariate
-BIDS_RFX(1, 6, 6);
-BIDS_RFX(2, 6, 6);
-
-BIDS_Results(6, 6, opt, 0);
-
-% subject level multivariate
-% isMVPA=1;
-% BIDS_FFX(1, 6, opt, isMVPA);
-% BIDS_FFX(2, 6, opt, isMVPA);
-% make4Dmaps(6,opt)
diff --git a/demo/batch_download_run.m b/demo/batch_download_run.m
deleted file mode 100644
index f9517cb9..00000000
--- a/demo/batch_download_run.m
+++ /dev/null
@@ -1,93 +0,0 @@
-% This script will download the dataset from the FIL for the block design SPM
-% tutorial and will run the basic preprocessing, FFX and contrasts on it.
-% Results might be a bit different from those in the manual as some
-% default options are slightly different in this pipeline (e.g use of FAST
-% instead of AR(1), motion regressors added)
-
-clear;
-clc;
-close all;
-
-% Smoothing to apply
-FWHM = 6;
-
-% URL of the data set to download
-URL = 'http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip';
-
-% 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 = getOption();
-
-% respecify options here in case the getOption file has been modified on
-% the repository
-
-% the dataset will downloaded and analysed there
-opt.dataDir = fullfile(WD, 'output', 'MoAEpilot');
-opt.groups = {''}; % no specific group
-opt.subjects = {[]}; % first subject
-opt.taskName = 'auditory'; % task to analyze
-
-% the following options are less important but are added to reset all
-% options
-opt.STC_referenceSlice = [];
-opt.sliceOrder = [];
-opt.funcVoxelDims = [];
-opt.jobsDir = fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL', 'JOBS', opt.taskName);
-
-% specify the model file that contains the contrasts to compute
-opt = rmfield(opt, 'model');
-opt.model.univariate.file = fullfile(WD, 'model-MoAE_smdl.json');
-
-% specify the result to compute
-opt.result.Steps(1) = struct( ...
- 'Level', 'subject', ...
- 'Contrasts', struct( ...
- 'Name', 'listening', ... % has to match
- 'Mask', false, ... % this might need improving if a mask is required
- 'MC', 'FWE', ... FWE, none, FDR
- 'p', 0.05, ...
- 'k', 0, ...
- 'NIDM', true));
-
-opt.result.Steps(1).Contrasts(2) = struct( ...
- 'Name', 'listening_inf_baseline', ...
- 'Mask', false, ...
- 'MC', 'none', ... FWE, none, FDR
- 'p', 0.01, ...
- 'k', 0, ...
- 'NIDM', true);
-
-%% Get data
-% fprintf('%-40s:', 'Downloading dataset...');
-% urlwrite(URL, 'MoAEpilot.zip');
-unzip('MoAEpilot.zip', fullfile(WD, 'output'));
-
-%% Check dependencies
-% If toolboxes are not in the MATLAB Directory and needed to be added to
-% the path
-
-% In case some toolboxes need to be added the matlab path, specify and uncomment
-% in the lines below
-
-% toolbox_path = '';
-% addpath(fullfile(toolbox_path)
-
-checkDependencies();
-
-%% Run batches
-isMVPA = 0;
-
-bidsCopyRawFolder(opt, 1);
-bidsSTC(opt);
-bidsSpatialPrepro(opt);
-bidsSmoothing(FWHM, opt);
-bidsFFX('specifyAndEstimate', opt, FWHM, isMVPA);
-bidsFFX('contrasts', opt, FWHM, isMVPA);
-bidsResults(opt, FWHM, [], isMVPA);
diff --git a/demos/MoAE/MoAEpilot_getOption.m b/demos/MoAE/MoAEpilot_getOption.m
new file mode 100644
index 00000000..dfe42339
--- /dev/null
+++ b/demos/MoAE/MoAEpilot_getOption.m
@@ -0,0 +1,44 @@
+% (C) Copyright 2019 Remi Gau
+
+function opt = MoAEpilot_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
+
+ % task to analyze
+ opt.taskName = 'auditory';
+
+ % The directory where the data are located
+ opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'output', 'MoAEpilot');
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')));
+
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ 'models', 'model-MoAE_smdl.json');
+
+ % specify the result to compute
+ opt.result.Steps(1) = struct( ...
+ 'Level', 'subject', ...
+ 'Contrasts', struct( ...
+ 'Name', 'listening', ... % has to match
+ 'Mask', false, ...
+ 'MC', 'FWE', ... FWE, none, FDR
+ 'p', 0.05, ...
+ 'k', 0, ...
+ 'NIDM', true));
+
+ opt.result.Steps(1).Contrasts(2) = struct( ...
+ 'Name', 'listening_inf_baseline', ...
+ 'Mask', false, ...
+ 'MC', 'none', ... FWE, none, FDR
+ 'p', 0.01, ...
+ 'k', 0, ...
+ 'NIDM', true);
+
+ %% DO NOT TOUCH
+ opt = checkOptions(opt);
+ saveOptions(opt);
+
+end
diff --git a/demos/MoAE/MoAEpilot_run.m b/demos/MoAE/MoAEpilot_run.m
new file mode 100644
index 00000000..1d9a065f
--- /dev/null
+++ b/demos/MoAE/MoAEpilot_run.m
@@ -0,0 +1,62 @@
+% (C) Copyright 2019 Remi Gau
+
+% This script will download the dataset from the FIL for the block design SPM
+% tutorial and will run the basic preprocessing, FFX and contrasts on it.
+% Results might be a bit different from those in the manual as some
+% default options are slightly different in this pipeline (e.g use of FAST
+% instead of AR(1), motion regressors added)
+
+clear;
+clc;
+
+% Smoothing to apply
+FWHM = 6;
+
+% URL of the data set to download
+URL = 'http://www.fil.ion.ucl.ac.uk/spm/download/data/MoAEpilot/MoAEpilot.bids.zip';
+
+% directory with this script becomes the current directory
+WD = fileparts(mfilename('fullpath'));
+
+% we add all the subfunctions that are in the sub directories
+addpath(genpath(fullfile(WD, '..', '..', 'src')));
+addpath(genpath(fullfile(WD, '..', '..', 'lib')));
+
+%% Set options
+opt = MoAEpilot_getOption();
+
+% Uncomment the line below to run preprocessing in "native" space.
+% - use realign and unwarp
+% - don't do normalization
+opt.space = 'individual';
+
+%% Get data
+fprintf('%-40s:', 'Downloading dataset...');
+urlwrite(URL, 'MoAEpilot.zip');
+unzip('MoAEpilot.zip', fullfile(WD, 'output'));
+
+checkDependencies();
+
+%% Run batches
+reportBIDS(opt);
+bidsCopyRawFolder(opt, 1);
+
+% In case you just want to run segmentation and skull stripping
+% Skull stripping is also included in 'bidsSpatialPrepro'
+% 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);
+
+% The following crash on Travis CI
+% bidsFFX('specifyAndEstimate', opt, FWHM);
+% bidsFFX('contrasts', opt, FWHM);
+% bidsResults(opt, FWHM);
diff --git a/demo/model-MoAE_smdl.json b/demos/MoAE/models/model-MoAE_smdl.json
similarity index 62%
rename from demo/model-MoAE_smdl.json
rename to demos/MoAE/models/model-MoAE_smdl.json
index a697396a..31c83ca4 100644
--- a/demo/model-MoAE_smdl.json
+++ b/demos/MoAE/models/model-MoAE_smdl.json
@@ -25,11 +25,31 @@
},
{
"Level": "run",
+ "Transformations": [
+ {
+ "Name": "Factor",
+ "Inputs": ["trial_type"]
+ },
+ {
+ "Name": "Convolve",
+ "Model": "spm",
+ "Inputs": ["trial_type.listening"]
+ }
+ ],
"Model": {
"X": [
"trial_type.listening",
"trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
- ]
+ ],
+ "Options": {
+ "high_pass_filter_cutoff_secs": 128
+ },
+ "Software": {
+ "SPM": {
+ "whitening": "AR(1)"
+ }
+ },
+ "Mask": ""
},
"AutoContrasts": ["trial_type.listening"]
}
diff --git a/demos/miss_hit.cfg b/demos/miss_hit.cfg
new file mode 100644
index 00000000..25947f2d
--- /dev/null
+++ b/demos/miss_hit.cfg
@@ -0,0 +1,12 @@
+# styly guide (https://florianschanda.github.io/miss_hit/style_checker.html)
+line_length: 100
+regex_function_name: "[a-zA-Z0-9]+(_*([a-zA-Z0-9]){1}[A-Za-z]+)*" # almost anything goes in the root folder
+copyright_entity: "Mohamed Rezk"
+copyright_entity: "Remi Gau"
+copyright_entity: "CPP BIDS SPM-pipeline developpers"
+
+# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
+metric "cnest": limit 4
+metric "file_length": limit 400
+metric "cyc": limit 12
+metric "parameters": limit 6
\ No newline at end of file
diff --git a/demos/openneuro/ds000001_getOption.m b/demos/openneuro/ds000001_getOption.m
new file mode 100644
index 00000000..0ff2bced
--- /dev/null
+++ b/demos/openneuro/ds000001_getOption.m
@@ -0,0 +1,24 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function opt = ds000001_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
+
+ % suject to run in each group
+ opt.subjects = {'01', '02'};
+
+ % task to analyze
+ opt.taskName = 'balloonanalogrisktask';
+
+ % The directory where the data are located
+ opt.dataDir = '/home/remi/openneuro/ds000001/raw';
+
+ %% DO NOT TOUCH
+ opt = checkOptions(opt);
+ saveOptions(opt);
+
+end
diff --git a/demos/openneuro/ds000001_run.m b/demos/openneuro/ds000001_run.m
new file mode 100644
index 00000000..c3da5d6d
--- /dev/null
+++ b/demos/openneuro/ds000001_run.m
@@ -0,0 +1,38 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+% runDs00014
+
+clear;
+clc;
+
+% 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(genpath(fullfile(WD, '..', '..', 'src')));
+addpath(genpath(fullfile(WD, '..', '..', 'lib')));
+
+%% Set options
+opt = ds000001_getOption();
+
+checkDependencies();
+
+reportBIDS(opt);
+
+bidsCopyRawFolder(opt, 1);
+
+bidsSTC(opt);
+
+bidsSpatialPrepro(opt);
+
+anatomicalQA(opt);
+
+bidsSmoothing(FWHM, opt);
+
+% Not implemented yet
+% bidsFFX('specifyAndEstimate', opt, FWHM);
+% bidsFFX('contrasts', opt, FWHM);
+% bidsResults(opt, FWHM);
diff --git a/demos/openneuro/ds000114_getOption.m b/demos/openneuro/ds000114_getOption.m
new file mode 100644
index 00000000..b3527bfc
--- /dev/null
+++ b/demos/openneuro/ds000114_getOption.m
@@ -0,0 +1,44 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function opt = ds000114_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/ds000114/raw';
+
+ % suject to run in each group
+ opt.subjects = {'01', '02'};
+
+ % task to analyze
+ opt.taskName = 'linebisection';
+
+ opt.anatReference.type = 'T1w';
+ opt.anatReference.session = 2;
+
+ opt.space = 'individual';
+
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ 'models', ...
+ 'model-ds000114-linebisection_smdl.json');
+
+ % specify the result to compute
+ opt.result.Steps(1) = struct( ...
+ 'Level', 'subject', ...
+ 'Contrasts', struct( ...
+ 'Name', 'Correct_Task', ... % has to match
+ 'Mask', false, ...
+ 'MC', 'FWE', ... FWE, none, FDR
+ 'p', 0.05, ...
+ 'k', 0, ...
+ 'NIDM', true));
+
+ %% DO NOT TOUCH
+ opt = checkOptions(opt);
+ saveOptions(opt);
+
+end
diff --git a/demos/openneuro/ds000114_run.m b/demos/openneuro/ds000114_run.m
new file mode 100644
index 00000000..1ccce4db
--- /dev/null
+++ b/demos/openneuro/ds000114_run.m
@@ -0,0 +1,41 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+% runDs00014
+
+clear;
+clc;
+
+% 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(genpath(fullfile(WD, '..', '..', 'src')));
+addpath(genpath(fullfile(WD, '..', '..', 'lib')));
+
+%% Set options
+opt = ds000114_getOption();
+
+checkDependencies();
+
+%% Run batches
+
+reportBIDS(opt);
+
+% bidsCopyRawFolder(opt, 1);
+%
+% bidsSTC(opt);
+
+bidsSpatialPrepro(opt);
+
+anatomicalQA(opt);
+bidsResliceTpmToFunc(opt);
+functionalQA(opt);
+
+bidsSmoothing(FWHM, opt);
+
+bidsFFX('specifyAndEstimate', opt, FWHM);
+bidsFFX('contrasts', opt, FWHM);
+bidsResults(opt, FWHM);
diff --git a/demos/openneuro/ds001168_getOption.m b/demos/openneuro/ds001168_getOption.m
new file mode 100644
index 00000000..028da8b9
--- /dev/null
+++ b/demos/openneuro/ds001168_getOption.m
@@ -0,0 +1,29 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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;
+
+ opt.space = 'individual';
+
+ %% 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..e8ab780c
--- /dev/null
+++ b/demos/openneuro/ds001168_run.m
@@ -0,0 +1,44 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+% runDs001168
+
+clear;
+clc;
+
+% 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(genpath(fullfile(WD, '..', '..', 'src')));
+addpath(genpath(fullfile(WD, '..', '..', 'lib')));
+
+%% Set options
+opt = ds001168_getOption();
+
+checkDependencies();
+
+%% Run batches
+
+reportBIDS(opt);
+
+bidsCopyRawFolder(opt, 1);
+
+bidsCreateVDM(opt);
+
+bidsSTC(opt);
+
+bidsSpatialPrepro(opt);
+
+anatomicalQA(opt);
+bidsResliceTpmToFunc(opt);
+functionalQA(opt);
+
+bidsSmoothing(FWHM, opt);
+
+% Not implemented yet
+% bidsFFX('specifyAndEstimate', opt, FWHM);
+% bidsFFX('contrasts', opt, FWHM);
+% bidsResults(opt, FWHM, []);
diff --git a/demos/openneuro/models/model-ds000114-linebisection_smdl.json b/demos/openneuro/models/model-ds000114-linebisection_smdl.json
new file mode 100644
index 00000000..31d58071
--- /dev/null
+++ b/demos/openneuro/models/model-ds000114-linebisection_smdl.json
@@ -0,0 +1,37 @@
+{
+ "Name": "linebisection",
+ "Description": "contrasts to compute for the line bisection task",
+ "Input": {
+ "task": "linebisection"
+ },
+ "Steps": [
+ {
+ "Level": "subject",
+ "Model": {
+ "X": [
+ "trial_type.Correct_Task",
+ "trial_type.Incorrect_Task",
+ "trial_type.No_Response_Task",
+ "trial_type.Response_Control",
+ "trial_type.No_Response_Control",
+ "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
+ ]
+ },
+ "AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"]
+ },
+ {
+ "Level": "run",
+ "Model": {
+ "X": [
+ "trial_type.Correct_Task",
+ "trial_type.Incorrect_Task",
+ "trial_type.No_Response_Task",
+ "trial_type.Response_Control",
+ "trial_type.No_Response_Control",
+ "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
+ ]
+ },
+ "AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/demos/openneuro/openneuro.md b/demos/openneuro/openneuro.md
new file mode 100644
index 00000000..c56643b6
--- /dev/null
+++ b/demos/openneuro/openneuro.md
@@ -0,0 +1,40 @@
+Download with datalad
+
+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.
+
+```bash
+datalad clone https://github.com/OpenNeuroDatasets/ds000114.git
+cd ds000114/
+datalad get sub-0[12]/*/anat/*
+datalad get sub-0[12]/*/func/*linebisection*
+```
+
+## ds000001
+
+```bash
+ datalad clone https://github.com/OpenNeuroDatasets/ds000001.git
+ cd ds000001/
+ 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/demos/vismotion/batch.m b/demos/vismotion/batch.m
new file mode 100644
index 00000000..75e9ff72
--- /dev/null
+++ b/demos/vismotion/batch.m
@@ -0,0 +1,55 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+clear;
+clc;
+
+% directory with this script becomes the current directory
+WD = fileparts(mfilename('fullpath'));
+
+% we add all the subfunctions that are in the sub directories
+addpath(genpath(fullfile(WD, '..', '..', 'src')));
+addpath(genpath(fullfile(WD, '..', '..', 'lib')));
+
+%% Run batches
+opt = getOption();
+
+checkDependencies();
+
+reportBIDS(opt);
+
+bidsCopyRawFolder(opt, 1);
+%
+% % preprocessing
+bidsSTC(opt);
+bidsSpatialPrepro(opt);
+
+% Quality control
+anatomicalQA(opt);
+
+% Not implemented yet
+% bidsResliceTpmToFunc(opt);
+% functionalQA(opt);
+
+funcFWHM = 6;
+bidsSmoothing(funcFWHM, opt);
+
+% subject level Univariate
+bidsFFX('specifyAndEstimate', opt, funcFWHM);
+bidsFFX('contrasts', opt, funcFWHM);
+
+% group level univariate
+conFWHM = 6;
+bidsRFX('smoothContrasts', funcFWHM, conFWHM, opt);
+
+% Not implemented yet
+% bidsRFX('RFX', funcFWHM, conFWHM, opt);
+
+% Not implemented yet
+% subject level multivariate
+% opt.model.file = fuufile(WD, ...
+% 'models', ...
+% 'model-motionDecodingMultivariate_smdl.json');
+%
+% bidsFFX('specifyAndEstimate', opt, funcFWHM);
+% bidsFFX('contrasts', opt, funcFWHM);
+% concatBetaImgTmaps(funcFWHM, opt);
diff --git a/demos/vismotion/getOption.m b/demos/vismotion/getOption.m
new file mode 100644
index 00000000..dca50692
--- /dev/null
+++ b/demos/vismotion/getOption.m
@@ -0,0 +1,39 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function opt = getOption()
+ % opt = 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
+
+ % task to analyze
+ opt.taskName = 'visMotion';
+
+ % The directory where the data are located
+ opt.dataDir = '/home/remi/gin/CPP_visMotion/raw';
+ opt.derivativesDir = '/home/remi/Documents';
+
+ % specify the model file that contains the contrasts to compute
+ opt.model.file = ...
+ '/home/remi/github/CPP_BIDS_SPM_pipeline/demos/vismotion/models/model-visMotionLoc_smdl.json';
+
+ % specify the result to compute
+ % Contrasts.Name has to match one of the contrast defined in the model json file
+ opt.result.Steps(1) = struct( ...
+ 'Level', 'dataset', ...
+ 'Contrasts', struct( ...
+ 'Name', 'VisMot_gt_VisStat', ... %
+ 'Mask', false, ...
+ 'MC', 'FWE', ... FWE, none, FDR
+ 'p', 0.05, ...
+ 'k', 0, ...
+ 'NIDM', true));
+
+ %% DO NOT TOUCH
+ opt = checkOptions(opt);
+ saveOptions(opt);
+
+end
diff --git a/demos/vismotion/models/model-motionDecodingMultivariate_smdl.json b/demos/vismotion/models/model-motionDecodingMultivariate_smdl.json
new file mode 100644
index 00000000..cbc82f59
--- /dev/null
+++ b/demos/vismotion/models/model-motionDecodingMultivariate_smdl.json
@@ -0,0 +1,43 @@
+{
+ "Name": "Motion Decoding",
+ "Description": "contrasts for the motion localizer dataset",
+ "Input": {
+ "task": "motionDecoding"
+ },
+ "Steps": [
+ {
+ "Level": "subject",
+ "AutoContrasts": [],
+ "Contrasts": []
+ },
+ {
+ "Level": "run",
+ "Model": {
+ "X": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L",
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_L",
+ "trial_type.Aud_R",
+ "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
+ ]
+ },
+ "AutoContrasts": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L",
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_R",
+ "trial_type.Aud_L"]
+ },
+ {
+ "Level": "dataset",
+ "AutoContrasts": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/demos/vismotion/models/model-motionDecodingUnivariate_smdl.json b/demos/vismotion/models/model-motionDecodingUnivariate_smdl.json
new file mode 100644
index 00000000..51977027
--- /dev/null
+++ b/demos/vismotion/models/model-motionDecodingUnivariate_smdl.json
@@ -0,0 +1,116 @@
+{
+ "Name": "Motion Decoding",
+ "Description": "contrasts for the motion localizer dataset",
+ "Input": {
+ "task": "motionDecoding"
+ },
+ "Steps": [
+ {
+ "Level": "subject",
+ "Model": {
+ "X": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L",
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_L",
+ "trial_type.Aud_R",
+ "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
+ ]
+ },
+ "AutoContrasts": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L",
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_R",
+ "trial_type.Aud_L"],
+ "Contrasts": [
+ {
+ "Name": "Vis",
+ "ConditionList": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L"
+ ],
+ "weights": [0.25, 0.25, 0.25, 0.25],
+ "type": "t"
+ },
+ {
+ "Name": "Aud",
+ "ConditionList": [
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_R",
+ "trial_type.Aud_L"
+ ],
+ "weights": [0.25, 0.25, 0.25, 0.25],
+ "type": "t"
+ },
+ {
+ "Name": "Vis_gt_Aud",
+ "ConditionList": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L",
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_R",
+ "trial_type.Aud_L"
+ ],
+ "weights": [
+ 0.25,
+ 0.25,
+ 0.25,
+ 0.25,
+ -0.25,
+ -0.25,
+ -0.25,
+ -0.25],
+ "type": "t"
+ },
+ {
+ "Name": "Aud_gt_Vis",
+ "ConditionList": [
+ "trial_type.Vis_U",
+ "trial_type.Vis_D",
+ "trial_type.Vis_R",
+ "trial_type.Vis_L",
+ "trial_type.Aud_U",
+ "trial_type.Aud_D",
+ "trial_type.Aud_R",
+ "trial_type.Aud_L"
+ ],
+ "weights": [
+ -0.25,
+ -0.25,
+ -0.25,
+ -0.25,
+ 0.25,
+ 0.25,
+ 0.25,
+ 0.25],
+ "type": "t"
+ }
+ ]
+ },
+ {
+ "Level": "dataset",
+ "AutoContrasts": [
+ "Vis_U",
+ "Vis_D",
+ "Vis_R",
+ "Vis_L",
+ "Aud_U",
+ "Aud_D",
+ "Aud_R",
+ "Aud_L"]
+ }
+ ]
+}
diff --git a/tests/dummyData/model/model-visMotionLoc_smdl.json b/demos/vismotion/models/model-visMotionLoc_smdl.json
similarity index 57%
rename from tests/dummyData/model/model-visMotionLoc_smdl.json
rename to demos/vismotion/models/model-visMotionLoc_smdl.json
index a1c21046..012d17a4 100644
--- a/tests/dummyData/model/model-visMotionLoc_smdl.json
+++ b/demos/vismotion/models/model-visMotionLoc_smdl.json
@@ -5,8 +5,25 @@
"task": "visMotion"
},
"Steps": [
+ {
+ "Level": "run",
+ "Model": {
+ "X": [
+ "trial_type.VisMot",
+ "trial_type.VisStat",
+ "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
+ ]
+ }
+ },
{
"Level": "subject",
+ "Model": {
+ "X": [
+ "trial_type.VisMot",
+ "trial_type.VisStat",
+ "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"
+ ]
+ },
"AutoContrasts": ["trial_type.VisMot", "trial_type.VisStat" ],
"Contrasts": [
{
@@ -29,7 +46,11 @@
},
{
"Level": "dataset",
- "AutoContrasts": ["trial_type.VisMot", "trial_type.VisStat", "VisMot_gt_VisStat", "VisStat_gt_VisMot"]
+ "AutoContrasts": [
+ "trial_type.VisMot",
+ "trial_type.VisStat",
+ "VisMot_gt_VisStat",
+ "VisStat_gt_VisMot"]
}
]
}
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..d0c3cbf1
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..24a540c3
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,108 @@
+# Setting up sphinx to create a matlab doc
+
+## Set up virtual environment
+
+```bash
+virtualenv -p python3 cpp_bids_spm
+source cpp_bids_spm/bin/activate
+
+pip install -r requirements.txt
+```
+
+## Quick start on the doc
+
+See the [sphinx doc](https://www.sphinx-doc.org/en/master/usage/quickstart.html)
+for more.
+
+This
+[blog post](https://medium.com/@richdayandnight/a-simple-tutorial-on-how-to-document-your-python-project-using-sphinx-and-rinohtype-177c22a15b5b)
+is also useful.
+
+```bash
+cd docs
+sphinx-quickstart # launch a basic interactive set up of sphinx
+```
+
+Answer the questions on prompt.
+
+## Setting up conf.py for matlab doc
+
+Following the documentation from
+[matlabdomain for sphinx](https://github.com/sphinx-contrib/matlabdomain).
+
+Specify the extensions you are using:
+
+```python
+extensions = [
+ 'sphinxcontrib.matlab',
+ '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:
+
+```python
+matlab_src_dir = os.path.dirname(os.path.abspath('../../src'))
+```
+
+## reStructured text markup
+
+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)
+
+- 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"
+
+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.
+
+```rst
+
+.. automodule:: src.folder_name .. <-- This is necessary for auto-documenting the rest
+
+.. autofunction:: function to document
+
+```
+
+To get the filenames of all the functions in a folder:
+
+``` bash
+ls -l src/*.m | cut -c42- | rev | cut -c 3- | rev
+```
+
+Increase the `42` to crop more characters at the beginning.
+
+Change the `3` to crop more characters at the end.
+
+## Build the documentation locally
+
+From the `docs` directory:
+
+```bash
+sphinx-build -b html source build
+```
+
+This will build an html version of the doc in the `build` folder.
+
+## Build the documentation with Read the Docs
+
+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 might need to be added as a maintainer of the doc.
+
+The doc can be built from any branch of a repo.
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 00000000..9534b018
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..42ea70da
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,5 @@
+Sphinx
+sphinxcontrib-matlabdomain
+sphinxcontrib-napoleon
+sphinx_rtd_theme
+miss_hit
\ No newline at end of file
diff --git a/docs/source/QA.rst b/docs/source/QA.rst
new file mode 100644
index 00000000..f636d60b
--- /dev/null
+++ b/docs/source/QA.rst
@@ -0,0 +1,11 @@
+Quality control workflows
+=========================
+
+Manual coregistration
+
+----
+
+.. automodule:: src.QA
+
+.. autofunction:: anatomicalQA
+.. autofunction:: functionalQA
diff --git a/docs/source/_static/cpp_lab_logo.png b/docs/source/_static/cpp_lab_logo.png
new file mode 100644
index 00000000..89277c01
Binary files /dev/null and b/docs/source/_static/cpp_lab_logo.png differ
diff --git a/docs/source/batches.rst b/docs/source/batches.rst
new file mode 100644
index 00000000..9da85ccb
--- /dev/null
+++ b/docs/source/batches.rst
@@ -0,0 +1,31 @@
+SPM batches
+===========
+
+List of functions to set SPM batches.
+
+----
+
+.. automodule:: src.batches
+
+.. 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:: setBatchSubjectLevelContrasts
+.. autofunction:: setBatchSubjectLevelGLMSpec
+
\ No newline at end of file
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 00000000..20cb5e99
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,91 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+sys.path.insert(0, os.path.abspath('../..'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'CPP BIDS SPM'
+copyright = '2020, the CPP BIDS SPM pipeline dev team'
+author = 'the CPP BIDS SPM pipeline dev team'
+
+# The full version, including alpha/beta/rc tags
+release = 'v0.1.0'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinxcontrib.matlab',
+ 'sphinx.ext.autodoc']
+matlab_src_dir = os.path.dirname(os.path.abspath('../../src'))
+primary_domain = 'mat'
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# The master toctree document.
+master_doc = 'index'
+
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+html_logo = '_static/cpp_lab_logo.png'
+
+# html_theme_options = {
+# 'github_user': 'cpp-lln-lab',
+# 'github_repo': 'CPP_BIDS_SPM_pipeline',
+# 'github_button': True,
+# 'github_banner': True
+# }
+# html_theme_options = {
+# 'collapse_navigation': False,
+# 'display_version': False,
+# 'navigation_depth': 4,
+# }
+
+html_sidebars = {
+ '**': [
+ 'about.html',
+ 'navigation.html',
+ 'relations.html', # needs 'show_related': True theme option to display
+ 'searchbox.html',
+ 'donate.html',
+ ]
+}
\ No newline at end of file
diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst
new file mode 100644
index 00000000..d9855ed0
--- /dev/null
+++ b/docs/source/contributing.rst
@@ -0,0 +1,140 @@
+Contributing
+************
+
+How to contribute to this project.
+
+
+Function templates
+==================
+
+.. automodule:: src.templates
+
+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::
+ %
+ % [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
+
+ end
+
+.. autofunction:: templateFunction
+
+
+----
+
+Numpy template
+--------------
+
+See more information `here
+`_
+
+::
+
+ function [argout] = templateFunction(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 output value
+
+ % The code goes below
+
+ end
+
+.. autofunction:: templateFunctionNumpy
+
+----
+
+Google template
+---------------
+
+See more information `there
+`_
+
+
+Example code in the help section
+--------------------------------
+
+::
+
+ % (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
+
+ % The code goes below
+
+.. autofunction:: templateFunctionExample
+
+
+
diff --git a/docs/source/defaults.rst b/docs/source/defaults.rst
new file mode 100644
index 00000000..85a1cc81
--- /dev/null
+++ b/docs/source/defaults.rst
@@ -0,0 +1,35 @@
+Pipeline defaults
+*****************
+
+Defaults of the pipeline.
+
+----
+
+.. automodule:: src.defaults
+
+checkOptions
+============
+
+.. autofunction:: checkOptions
+
+spm_my_defaults
+===============
+
+Some more SPM options can be set in the ``spm_my_defaults.m``.
+
+auto-correlation modelisation
+-----------------------------
+
+Use of FAST and not AR1 for auto-correlation modelisation.
+
+Using FAST does not seem to affect results on time series with "normal" TRs but
+improves results when using sequences: it is therefore used by default in this
+pipeline.
+
+> Olszowy, W., Aston, J., Rua, C. et al. Accurate autocorrelation modeling
+> substantially improves fMRI reliability. Nat Commun 10, 1220 (2019).
+> https://doi.org/10.1038/s41467-019-09230-w
+
+.. autofunction:: spm_my_defaults
+
+
diff --git a/docs/source/docker.rst b/docs/source/docker.rst
new file mode 100644
index 00000000..eb55b006
--- /dev/null
+++ b/docs/source/docker.rst
@@ -0,0 +1,35 @@
+Docker
+******
+
+The recipe to build the docker image is in the ``Dockerfile``
+
+Build docker image
+==================
+
+To build the image with with octave and SPM the `Dockerfile` just type::
+
+ docker build -t cpp_spm:0.0.1 .
+
+This will create an image with the tag name `cpp_spm_octave:0.0.1`
+
+Run docker image
+================
+
+The following code would start the docker image and would map 2 folders one for
+``output`` and one for ``code`` you want to run::
+
+ docker run -it --rm \
+ -v [output_folder]:/output \
+ -v [code_folder]:/code cpp_spm:0.0.1
+
+To test it you can copy the ``MoAEpilot_run.m`` file in the ``demos/MoAE`` folder on
+your computer and then start running the docker and type::
+
+ # to change to the code folder inside the container
+ # running the command 'ls' should show MoAEpilot_run.m and the content of
+ # the folder
+ cd demos/MoAE
+
+ # To run the batch_download_run script
+ octave --no-gui --eval MoAEpilot_run
+
diff --git a/docs/source/function_description.rst b/docs/source/function_description.rst
new file mode 100644
index 00000000..10745525
--- /dev/null
+++ b/docs/source/function_description.rst
@@ -0,0 +1,76 @@
+Function description
+********************
+
+List of functions in the ``src`` folder.
+
+----
+
+.. automodule:: src
+
+.. autofunction:: getAnatFilename
+.. autofunction:: getBoldFilename
+.. autofunction:: getData
+.. autofunction:: getFuncVoxelDims
+.. autofunction:: getInfo
+.. autofunction:: getMeanFuncFilename
+.. autofunction:: getPrefix
+.. autofunction:: getRealignParamFile
+.. autofunction:: getSliceOrder
+.. autofunction:: getSpecificSubjects
+.. autofunction:: setDerivativesDir
+.. autofunction:: unzipImgAndReturnsFullpathName
+
+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:: saveOptions
+.. autofunction:: setDefaultFields
+.. autofunction:: validationInputFile
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 00000000..aafb80c3
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,68 @@
+.. cpp_bids_spm documentation master file, created by
+ sphinx-quickstart on Tue Oct 13 11:38:30 2020.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to CPP SPM BIDS pipeline documentation!
+***********************************************
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Content
+
+ set_up
+ workflows
+ QA
+ defaults
+ batches
+ function_description
+ method_section_boilerplate
+ mancoreg
+ docker
+ contributing
+
+This pipeline contains a set of functions to run fMRI analysis on a
+[BIDS data set](https://bids.neuroimaging.io/) using SPM12.
+
+This can perform:
+
+- slice timing correction,
+- spatial preprocessing:
+
+ - realignment, coregistration `func` to `anat`, `anat` segmention,
+ normalization to MNI space
+ - realignm and unwarp, coregistration `func` to `anat`, `anat` segmention
+
+- 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).
+
+It can also prepare the data to run an MVPA analysis by running a GLM for each
+subject on non-normalized images and get one beta image for each condition to be
+used in the MVPA.
+
+
+Assumptions
+===========
+
+At the moment this pipeline makes some assumptions:
+
+- it assumes that the dummy scans have been removed from the BIDS data set and
+ it can jump straight into pre-processing,
+- it assumes the metadata for a given task are the same as those the first run
+ of the first subject this pipeline is being run on,
+- it assumes that group are defined in the subject field (eg `sub-ctrl01`,
+ `sub-blind01`, ...) and not in the `participants.tsv` file.
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/source/mancoreg.rst b/docs/source/mancoreg.rst
new file mode 100644
index 00000000..5dacf6f9
--- /dev/null
+++ b/docs/source/mancoreg.rst
@@ -0,0 +1,11 @@
+Manual coregistration
+=====================
+
+Manual coregistration tools
+
+----
+
+.. automodule:: src.mancoreg
+
+.. autofunction:: mancoreg
+.. autofunction:: mancoregCallbacks
diff --git a/docs/source/method_section_boilerplate.rst b/docs/source/method_section_boilerplate.rst
new file mode 100644
index 00000000..bb50cd93
--- /dev/null
+++ b/docs/source/method_section_boilerplate.rst
@@ -0,0 +1,89 @@
+Boilerplate methods section
+***************************
+
+Preprocessing
+=============
+
+The fMRI data were pre-processed and analyzed using statistical parametric
+mapping (SPM12 – v7487; Wellcome Center for Neuroimaging, London, UK;
+www.fil.ion.ucl.ac.uk/spm) running on {octave 4.{??} / matlab 20{XX}
+(Mathworks)}.
+
+The preprocessing of the functional images was performed in the following order:
+removing of dummy scans, {slice timing correction}, realignment, normalization
+to MNI, smoothing.
+
+{XX} dummy scans were removed to allow signal stabilization.
+
+{Slice timing correction was then performed taking the {XX} th slice as a
+reference (interpolation: sinc interpolation).}
+
+Functional scans from each participant were realigned using the mean image as a
+reference (SPM 2 passes ; number of degrees of freedom: 6 ; cost function: least
+square) (Friston et al, 1995).
+
+The mean image obtained from realignement was then co-registered to the
+anatomical T1 image (number of degrees of freedom: 6 ; cost function: normalized
+mutual information) (Friston et al, 1995). The transformation matrix from this
+coregistration was then applied to all the functional images.
+
+The anatomical T1 image was bias field corrected, segmented and normalized to
+MNI space (target space resolution: 1 mm ; interpolation: 4th degree b-spline)
+using a unified segmentation. The deformation field obtained from this step was
+then applied to all the functional images (target space resolution equal that
+used at acquisition ; interpolation: 4th degree b-spline)
+
+Functional MNI normalized images were then spatially smoothed using a 3D
+gaussian kernel (FWHM = {XX} mm).
+
+fMRI data analysis
+==================
+
+At the subject level, we performed a mass univariate analysis with a linear
+regression at each voxel of the brain, using generalized least squares with a
+global FAST model to account for temporal auto-correlation (Corbin et al, 2018)
+and a drift fit with discrete cosine transform basis (128 seconds cut-off).
+Image intensity scaling was done run-wide before statistical modeling such that
+the mean image will have mean intracerebral intensity of 100.
+
+We modeled the fMRI experiment in an event related design with regressors
+entered into the run-specific design matrix after convolving the onsets of each
+event with a canonical hemodynamic response function (HRF).
+
+.. todo:
+ Table of conditions with duration of each event
+
+Nuisance covariates included the 6 realignment parameters to account for
+residual motion artefacts.
+
+Contrast images were computed for the following condition and spatially smoothed
+using a 3D gaussian kernel (FWHM = {XX} mm).
+
+.. todo:
+ Table of constrast with weight
+
+Group level
+===========
+
+WIP
+
+References
+==========
+
+Friston KJ, Ashburner J, Frith CD, Poline J-B, Heather JD & Frackowiak RSJ
+(1995) Spatial registration and normalization of images Hum. Brain Map.
+2:165-189
+
+Corbin, N., Todd, N., Friston, K. J. & Callaghan, M. F. Accurate modeling of
+temporal correlations in rapidly sampled fMRI time series. Hum. Brain Mapp. 39,
+3884–3897 (2018).
+
+
+---
+
+Use the report function to get a print out of the content of a dataset.
+
+.. automodule:: src.reports
+
+.. autofunction:: reportBIDS
+.. autofunction:: copyGraphWindownOutput
\ No newline at end of file
diff --git a/docs/source/set_up.rst b/docs/source/set_up.rst
new file mode 100644
index 00000000..33c1fbfa
--- /dev/null
+++ b/docs/source/set_up.rst
@@ -0,0 +1,108 @@
+Set up
+******
+
+Configuration of the pipeline
+=============================
+
+All the details specific to your analysis should be set in the `getOptions.m`.
+
+There is a getOption_template file that shows you would set up the getOption
+file if one wanted to analyse the
+`ds001 data set from OpenNeuro `_.
+
+Set the group of subjects to analyze::
+
+ opt.groups = {'control', 'blind'};
+
+If there are no groups (i.e subjects names are of the form ``sub-01`` for example)
+or if you want to run all subjects of all groups then use::
+
+ opt.groups = {''};
+ opt.subjects = {[]};
+
+If you have 2 groups (``cont`` and ``cat`` for example) the following will run
+cont01, cont02, cat03, cat04::
+
+ opt.groups = {'cont', 'cat'};
+ opt.subjects = {[1 2], [3 4]};
+
+If you have more than 2 groups but want to only run the subjects of 2 groups
+then you can use::
+
+ opt.groups = {'cont', 'cat'};
+ opt.subjects = {[], []};
+
+You can also directly specify the subject label for the participants you want to
+run::
+
+ opt.groups = {''};
+ opt.subjects = {'01', 'cont01', 'cat02', 'ctrl02', 'blind01'};
+
+Set the task to analyze in the BIDS data set ``opt.taskName = 'auditory'``
+
+
+BIDS model JSON files
+=====================
+
+This files allow you to specify the GLM to run and which contrasts to run.
+It follows the BIDS statistical model extension and as implemented by
+`fitlins `_
+
+The model json file that describes:
+
+- out to prepare the regressors for the GLM: `Transformation`
+- the design matrix: `X`
+- the contrasts to compute: `contrasts`
+
+It also allows to specify those for different levels of the analysis:
+
+- run
+- session
+- subject
+- dataset
+
+An example of json file could look something like that::
+
+ {
+ "Name": "Basic",
+ "Description": "",
+ "Input": {
+ "task": "motionloc"
+ },
+ "Steps": [
+ {
+ "Level": "subject",
+ "AutoContrasts": ["stim_type.motion", "stim_type.static"],
+ "Contrasts": [
+ {
+ "Name": "motion_vs_static",
+ "ConditionList": ["stim_type.motion", "stim_type.static"],
+ "weights": [1, -1],
+ "type": "t"
+ }
+ ]
+ },
+ {
+ "Level": "dataset",
+ "AutoContrasts": [
+ "stim_type.motion",
+ "stim_type.static",
+ "motion_vs_static"
+ ]
+ }
+ ]
+ }
+
+
+In brief this means:
+
+- at the subject level automatically compute the t contrast against baseline
+ for the condition ``motion`` and ``static`` and compute the t-contrats for motion
+ VS static with these given weights.
+- at the level of the data set (so RFX) do the t contrast of the ``motion``,
+ ``static``, ``motion VS static``.
+
+We are currently using this to run different subject level GLM models for our
+univariate and multivariate analysis where in the first one we compute a con
+image that averages the beta image of all the runs where as in the latter case
+we get one con image for each run.
\ No newline at end of file
diff --git a/docs/source/workflows.rst b/docs/source/workflows.rst
new file mode 100644
index 00000000..21f5872c
--- /dev/null
+++ b/docs/source/workflows.rst
@@ -0,0 +1,122 @@
+Workflows
+*********
+
+.. automodule:: src.workflows
+
+List of the different workflows of the pipeline.
+
+Each has to be run for each task independently. All parameters should preferably
+be changed in the `opt` structure.
+
+See the set up section.
+
+----
+
+.. autofunction:: bidsCopyRawFolder
+
+.. autofunction:: bidsResliceTpmToFunc
+
+
+Slice Time Correction
+=====================
+
+.. autofunction:: bidsSTC
+
+Performs Slice Time Correction (STC) of the functional volumes by running the script:
+``bidsSTC.m``
+
+STC will be performed using the information provided in the BIDS data set. It
+will use the mid-volume acquisition time point as as reference.
+
+The ``getOption.m`` fields related to STC can still be used to do some slice
+timing correction even no information is can be found in the BIDS data set.
+
+In general slice order and reference slice is 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 available on this page of the
+`SPM wikibook `_.
+
+----
+
+Some comments from `here `_ on STC, when
+it should be applied
+
+*At what point in the processing stream should you use it?*
+
+*This is the great open question about slice timing, and it's not
+super-answerable. Both SPM and AFNI recommend you do it before doing
+realignment/motion correction, but it's not entirely clear why. The issue is
+this:*
+
+*If you do slice timing correction before realignment, you might look down your
+non-realigned time course for a given voxel on the border of gray matter and
+CSF, say, and see one TR where the head moved and the voxel sampled from CSF
+instead of gray. This would results in an interpolation error for that voxel, as
+it would attempt to interpolate part of that big giant signal into the previous
+voxel. On the other hand, if you do realignment before slice timing correction,
+you might shift a voxel or a set of voxels onto a different slice, and then
+you'd apply the wrong amount of slice timing correction to them when you
+corrected - you'd be shifting the signal as if it had come from slice 20, say,
+when it actually came from slice 19, and shouldn't be shifted as much.*
+
+*There's no way to avoid all the error (short of doing a four-dimensional
+realignment process combining spatial and temporal correction - Remi's note:
+fMRIprep does it), but I believe the current thinking is that doing slice timing
+first minimizes your possible error. The set of voxels subject to such an
+interpolation error is small, and the interpolation into another TR will also be
+small and will only affect a few TRs in the time course. By contrast, if one
+realigns first, many voxels in a slice could be affected at once, and their
+whole time courses will be affected. I think that's why it makes sense to do
+slice timing first. That said, here's some articles from the SPM e-mail list
+that comment helpfully on this subject both ways, and there are even more if you
+do a search for "slice timing AND before" in the archives of the list.*
+
+Spatial Preprocessing
+=====================
+
+Performs spatial preprocessing by running the script: ``bidsSpatialPrepro.m``
+
+.. autofunction:: bidsSpatialPrepro
+
+Smoothing
+=========
+Performs smoothing of the functional data by running the function: ``bidsSmoothing.m``
+
+.. autofunction:: bidsSmoothing
+
+Subject level analysis
+======================
+
+Performs the subject level analysis by running the ffx script: ``bidsFFX.m``.
+
+This will run twice, once for model specification and another time for model
+estimation. See the function for more details.
+
+This will take each condition present in the ``events.tsv`` file of each run and
+convolve it with a canonical HRF. It will also add the 6 realignment parameters
+of every run as confound regressors.
+
+.. autofunction:: bidsFFX
+
+Group level analysis
+====================
+
+Performs the group level analysis by running the RFX script: ``bidsRFX.m``
+
+.. autofunction:: bidsRFX
+
+Comput results
+==============
+
+.. autofunction:: bidsResults
+
+
+---
+
+.. autofunction:: bidsRealignReslice
+.. autofunction:: bidsRealignUnwarp
+.. autofunction:: bidsCreateVDM
+.. autofunction:: bidsResliceTpmToFunc
+.. autofunction:: bidsSegmentSkullStrip
diff --git a/getOption.m b/getOption.m
deleted file mode 100644
index 468b2dd4..00000000
--- a/getOption.m
+++ /dev/null
@@ -1,79 +0,0 @@
-function opt = getOption()
- % __ ____ ____ _ _ _
- % / _)( _ \( _ \ | | / \ | )
- % ( (_ )___/ )___/ | |_ / _ \ | \
- % \__)(__) (__) |___||_/ \_||__)
- %
- % Thank you for using the CPP lap pipeline - version 0.0.3.
- %
- % Current list of contributors includes
- % Mohamed Rezk
- % Rémi Gau
- % Olivier Collignon
- % Ane Gurtubay
- % Marco Barilari
- %
- % Please cite using the following DOI:
- % https://doi.org/10.5281/zenodo.3554332
- %
- % For bug report, suggestions for improvements or contributions see our github repo:
- % https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline
-
- % 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
-
- % group of subjects to analyze
- opt.groups = {''};
- % suject to run in each group
- opt.subjects = {[]};
-
- % task to analyze
- opt.taskName = 'visMotion';
-
- % The directory where the data are located
- opt.dataDir = '/home/remi/BIDS/visMotion/derivatives/';
-
- % specify the model file that contains the contrasts to compute
- opt.model.univariate.file = ...
- '/home/remi/github/CPP_BIDS_SPM_pipeline/model-visMotionLoc_smdl.json';
- opt.model.multivariate.file = '';
-
- % specify the result to compute
- % Contrasts.Name has to match one of the contrast defined in the model json file
- opt.result.Steps(1) = struct( ...
- 'Level', 'dataset', ...
- 'Contrasts', struct( ...
- 'Name', 'VisMot_gt_VisStat', ... %
- 'Mask', false, ... % this might need improving if a mask is required
- 'MC', 'FWE', ... FWE, none, FDR
- 'p', 0.05, ...
- 'k', 0, ...
- 'NIDM', true));
-
- % 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 = [];
-
- % Suffix output directory for the saved jobs
- opt.jobsDir = fullfile( ...
- opt.dataDir, '..', 'derivatives', ...
- 'SPM12_CPPL', 'JOBS', opt.taskName);
-
- % Save the opt variable as a mat file to load directly in the preprocessing
- % scripts
- save('opt.mat', 'opt');
-
-end
diff --git a/getOption_template b/getOption_template
deleted file mode 100644
index 25356e00..00000000
--- a/getOption_template
+++ /dev/null
@@ -1,63 +0,0 @@
-function opt = 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
-
-% 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 = [];
-
-
-% Suffix output directory for the saved jobs
-opt.JOBS_dir = fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL', 'JOBS', opt.taskName);
-
-% 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.multivariate.file = fullfile(fileparts(mfilename('fullpath')), 'model', model-balloonanalogriskMultivariate_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) );
-
-
-% Save the opt variable as a mat file to load directly in the preprocessing
-% scripts
-save('opt.mat','opt')
-
-end
diff --git a/lib/bids-matlab b/lib/bids-matlab
new file mode 160000
index 00000000..cc946cf7
--- /dev/null
+++ b/lib/bids-matlab
@@ -0,0 +1 @@
+Subproject commit cc946cf7d63c5ad42aa7258ef4ef73a2d24c97e5
diff --git a/lib/spmup b/lib/spmup
new file mode 160000
index 00000000..198c980d
--- /dev/null
+++ b/lib/spmup
@@ -0,0 +1 @@
+Subproject commit 198c980d6d7520b1a996f0e56269e2ceab72cc83
diff --git a/miss_hit.cfg b/miss_hit.cfg
index aa91fe79..2c6a2b5c 100644
--- a/miss_hit.cfg
+++ b/miss_hit.cfg
@@ -1,11 +1,19 @@
# styly guide (https://florianschanda.github.io/miss_hit/style_checker.html)
line_length: 100
regex_function_name: "[a-z]+(_*([a-zA-Z0-9]){1}[A-Za-z]+)*" # almost anything goes in the root folder
-suppress_rule: "copyright_notice"
exclude_dir: "lib"
+copyright_entity: "JH"
+copyright_entity: "DSS"
+copyright_entity: "Agah Karakuzu"
+copyright_entity: "Olivier Collignon"
+copyright_entity: "Mohamed Rezk"
+copyright_entity: "Remi Gau"
+copyright_entity: "CPP BIDS SPM-pipeline developers"
+
+tab_width: 2
# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
metric "cnest": limit 4
-metric "file_length": limit 300
-metric "cyc": limit 15
-metric "parameters": limit 4
\ No newline at end of file
+metric "file_length": limit 400
+metric "cyc": limit 12
+metric "parameters": limit 6
\ No newline at end of file
diff --git a/model-motionDecodingMultivariate_smdl.json b/model-motionDecodingMultivariate_smdl.json
deleted file mode 100644
index 4750018a..00000000
--- a/model-motionDecodingMultivariate_smdl.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "Name": "Motion Decoding",
- "Description": "contrasts for the motion localizer dataset",
- "Input": {
- "task": "motionDecoding"
- },
- "Steps": [
- {
- "Level": "subject",
- "AutoContrasts": [],
- "Contrasts": []
- },
- {
- "Level": "run",
- "AutoContrasts": ["trial_type.Vis_U", "trial_type.Vis_D", "trial_type.Vis_R", "trial_type.Vis_L","trial_type.Aud_U", "trial_type.Aud_D", "trial_type.Aud_R", "trial_type.Aud_L"]
- },
- {
- "Level": "dataset",
- "AutoContrasts": []
- }
- ]
-}
diff --git a/model-motionDecodingUnivariate_smdl.json b/model-motionDecodingUnivariate_smdl.json
deleted file mode 100644
index a691e0ab..00000000
--- a/model-motionDecodingUnivariate_smdl.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "Name": "Motion Decoding",
- "Description": "contrasts for the motion localizer dataset",
- "Input": {
- "task": "motionDecoding"
- },
- "Steps": [
- {
- "Level": "subject",
- "AutoContrasts": ["trial_type.Vis_U", "trial_type.Vis_D","trial_type.Vis_R", "trial_type.Vis_L","trial_type.Aud_U", "trial_type.Aud_D","trial_type.Aud_R", "trial_type.Aud_L"],
- "Contrasts": [
- {
- "Name": "Vis",
- "ConditionList": [
- "trial_type.Vis_U", "trial_type.Vis_D", "trial_type.Vis_R", "trial_type.Vis_L"
- ],
- "weights": [0.25 0.25 0.25 0.25],
- "type": "t"
- }
- {
- "Name": "Aud",
- "ConditionList": [
- "trial_type.Aud_U", "trial_type.Aud_D", "trial_type.Aud_R", "trial_type.Aud_L"
- ],
- "weights": [0.25 0.25 0.25 0.25],
- "type": "t"
- }
- {
- "Name": "Vis_gt_Aud",
- "ConditionList": [
- "trial_type.Vis_U", "trial_type.Vis_D", "trial_type.Vis_R", "trial_type.Vis_L", "trial_type.Aud_U", "trial_type.Aud_D", "trial_type.Aud_R", "trial_type.Aud_L"
- ],
- "weights": [0.25 0.25 0.25 0.25 -0.25 -0.25 -0.25 -0.25],
- "type": "t"
- }
- {
- "Name": "Aud_gt_Vis",
- "ConditionList": [
- "trial_type.Vis_U", "trial_type.Vis_D", "trial_type.Vis_R", "trial_type.Vis_L", "trial_type.Aud_U", "trial_type.Aud_D", "trial_type.Aud_R", "trial_type.Aud_L"
- ],
- "weights": [-0.25 -0.25 -0.25 -0.25 0.25 0.25 0.25 0.25],
- "type": "t"
- }
- ]
- },
- {
- "Level": "dataset",
- "AutoContrasts": ["Vis_U", "Vis_D","Vis_R", "Vis_L","Aud_U", "Aud_D","Aud_R", "Aud_L"]
- }
- ]
-}
diff --git a/model/model-balloonanalogriskMultivariate_smdl.json b/model/model-balloonanalogriskMultivariate_smdl.json
deleted file mode 100644
index 2827c98b..00000000
--- a/model/model-balloonanalogriskMultivariate_smdl.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "Name": "balloonanalogrisk",
- "Description": "contrasts for the balloonanalogrisk dataset",
- "Input": {
- "task": "balloonanalogrisktask"
- },
- "Steps": [
- {
- "Level": "subject",
- "AutoContrasts": [],
- "Contrasts": []
- },
- {
- "Level": "run",
- "AutoContrasts": ["trial_type.pumps_demean"]
- },
- {
- "Level": "dataset",
- "AutoContrasts": []
- }
- ]
-}
diff --git a/model/model-balloonanalogriskUnivariate_smdl.json b/model/model-balloonanalogriskUnivariate_smdl.json
deleted file mode 100644
index e4205f6a..00000000
--- a/model/model-balloonanalogriskUnivariate_smdl.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "Name": "balloonanalogrisk",
- "Description": "contrasts for the balloonanalogrisk dataset",
- "Input": {
- "task": "balloonanalogrisktask"
- },
- "Steps": [
- {
- "Level": "run",
- "AutoContrasts": [],
- "Contrasts": []
- },
- {
- "Level": "subject",
- "AutoContrasts": ["trial_type.pumps_demean", "trial_type.cash_demean"]
- },
- {
- "Level": "dataset",
- "AutoContrasts": ["pumps_demean"]
- }
- ]
-}
diff --git a/npm-requirements.txt b/npm-requirements.txt
new file mode 100644
index 00000000..061f9ade
--- /dev/null
+++ b/npm-requirements.txt
@@ -0,0 +1,6 @@
+remark-cli@5.0.0
+remark-lint@6.0.2
+remark-preset-lint-recommended@3.0.2
+remark-preset-lint-markdown-style-guide@2.1.2
+remark-preset-lint-consistent
+
diff --git a/runTests.m b/runTests.m
index 43f95a47..ac531bf4 100644
--- a/runTests.m
+++ b/runTests.m
@@ -1,8 +1,6 @@
-warning('OFF');
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developpers
-addpath(fullfile(pwd, 'spm12'));
-addpath(genpath(fullfile(pwd, 'src')));
-addpath(genpath(fullfile(pwd, 'lib')));
+warning('OFF');
spm('defaults', 'fMRI');
@@ -10,14 +8,14 @@
testFolder = fullfile(pwd, 'tests');
success = moxunit_runtests( ...
- testFolder, ...
- '-verbose', '-recursive', '-with_coverage', ...
- '-cover', folderToCover, ...
- '-cover_xml_file', 'coverage.xml', ...
- '-cover_html_dir', fullfile(pwd, 'coverage_html'));
+ testFolder, ...
+ '-verbose', '-recursive', '-with_coverage', ...
+ '-cover', folderToCover, ...
+ '-cover_xml_file', 'coverage.xml', ...
+ '-cover_html_dir', fullfile(pwd, 'coverage_html'));
if success
- system('echo 0 > test_report.log');
+ system('echo 0 > test_report.log');
else
- system('echo 1 > test_report.log');
+ system('echo 1 > test_report.log');
end
diff --git a/spm_my_defaults.m b/spm_my_defaults.m
deleted file mode 100644
index 1a4e3ed3..00000000
--- a/spm_my_defaults.m
+++ /dev/null
@@ -1,223 +0,0 @@
-function spm_my_defaults
- % This is where we set the defautls we want to use.
- % These will overide the spm defaults.
- % When "not enough" information is specified in the batch files, SPM falls
- % back on the defaults to fill in the blanks. This allows to make the
- % script simpler.
-
- global defaults
-
- % Realignment defaults
- % ==========================================================================
- defaults.realign.estimate.quality = 1;
- defaults.realign.estimate.interp = 2;
- defaults.realign.estimate.wrap = [0 0 0];
- defaults.realign.estimate.sep = 2;
- defaults.realign.estimate.fwhm = 5;
- defaults.realign.estimate.rtm = 1;
- defaults.realign.write.mask = 1;
- defaults.realign.write.interp = 3;
- defaults.realign.write.wrap = [0 0 0];
- defaults.realign.write.which = [0 1];
-
- % Coregistration defaults
- % ==========================================================================
- defaults.coreg.estimate.cost_fun = 'nmi';
- defaults.coreg.estimate.sep = [4 2];
- defaults.coreg.estimate.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];
- defaults.coreg.estimate.fwhm = [7 7];
-
- % Spatial Normalisation defaults
- % ==========================================================================
- defaults.normalise.write.preserve = 0;
- defaults.normalise.write.bb = [[-78 -112 -70]; [78 76 85]];
- defaults.normalise.write.interp = 4;
-
- % fMRI design defaults
- % ==========================================================================
- defaults.stats.fmri.t = 16;
- defaults.stats.fmri.t0 = 8;
- defaults.stats.fmri.hpf = 128;
- defaults.stats.fmri.cvi = 'FAST';
-
- % Filename prefix defaults
- % ==========================================================================
- defaults.slicetiming.prefix = 'a';
- defaults.realign.write.prefix = 'r';
- defaults.coreg.write.prefix = 'r';
- defaults.unwarp.write.prefix = 'u';
- defaults.normalise.write.prefix = 'w';
- defaults.deformations.modulate.prefix = 'm';
- defaults.smooth.prefix = 's';
- defaults.imcalc.prefix = 'i';
-
- % Set the defaults which are used by SPM
- % __________________________________________________________________________
- %
- % If you want to customise some defaults for your installation, do not
- % modify this file directly, but create a file named spm_my_defaults.m
- % instead, accessible from MATLAB search path; e.g., it can be saved in
- % MATLAB Startup Folder: userhome/Documents/MATLAB.
- %
- % Example: create the following file to change the image file extension:
- % ----------- file /home/karl/Documents/MATLAB/spm_my_defaults.m -----------
- % global defaults
- % defaults.images.format = 'img';
- % --------------------------------------------------------------------------
- %
- % spm_defaults should not be called directly in any script or function
- % (apart from SPM internals).
- % To load the defaults, use spm('Defaults',modality).
- % To get/set the defaults, use spm_get_defaults.
-
- % % User Interface defaults
- % %==========================================================================
- % defaults.ui.monitor = NaN;
- % defaults.ui.colour = [0.58 0.77 0.57];
- % defaults.ui.fs = 14; % unused
- % defaults.ui.print = 'ps';
- % defaults.renderer = 'opengl';
- %
- % % File format specific
- % %==========================================================================
- % % Note that defaults.analyze.flip is no longer used. Specifying the
- % % left/right handedness of the voxel indices is now done entirely by
- % % spm_flip_analyze_images.m
- % defaults.images.format = 'nii'; % options: 'img', 'nii'
- % defaults.mat.format = '-v6'; % options: '-mat', '-v6', '-v7.0', '-v7.3'
- %
- % % Toolboxes defaults
- % %==========================================================================
- % defaults.tbx.dir = { fullfile(spm('Dir'),'toolbox') };
- %
- % % DICOM Import defaults
- % %==========================================================================
- % defaults.dicom.root = 'flat'; % Folder hierarchy
- %
- % % fMRI design defaults
- % %==========================================================================
- % defaults.stats.fmri.t = 16;
- % defaults.stats.fmri.t0 = 8;
- % defaults.stats.fmri.hpf = 128;
- % defaults.stats.fmri.cvi = 'AR(1)';
- % defaults.stats.fmri.hrf = [6 16 1 1 6 0 32];
- %
- % % Mask defaults
- % %==========================================================================
- % defaults.mask.thresh = 0.8;
- %
- % % Stats defaults
- % %==========================================================================
- % defaults.stats.maxmem = 2^29;
- % defaults.stats.maxres = 64;
- % defaults.stats.resmem = false;
- % defaults.stats.fmri.ufp = 0.001; % Upper tail F-probability
- % defaults.stats.pet.ufp = 0.05;
- % defaults.stats.eeg.ufp = 0.05;
- % defaults.stats.topoFDR = 1;
- % defaults.stats.rft.nonstat = 0;
- % defaults.stats.results.volume.distmin = 8;
- % defaults.stats.results.volume.nbmax = 3;
- % defaults.stats.results.svc.distmin = 4;
- % defaults.stats.results.svc.nbmax = 16;
- % defaults.stats.results.mipmat = {fullfile(spm('dir'),'MIP.mat')};
- %
- % % Filename prefix defaults
- % %==========================================================================
- % defaults.slicetiming.prefix = 'a';
- % defaults.realign.write.prefix = 'r';
- % defaults.coreg.write.prefix = 'r';
- % defaults.unwarp.write.prefix = 'u';
- % defaults.normalise.write.prefix = 'w';
- % defaults.deformations.modulate.prefix = 'm';
- % defaults.smooth.prefix = 's';
- % defaults.imcalc.prefix = 'i';
- %
- % % Realignment defaults
- % %==========================================================================
- % defaults.realign.estimate.quality = 0.9;
- % defaults.realign.estimate.interp = 2;
- % defaults.realign.estimate.wrap = [0 0 0];
- % defaults.realign.estimate.sep = 4;
- % defaults.realign.estimate.fwhm = 5;
- % defaults.realign.estimate.rtm = 1;
- % defaults.realign.write.mask = 1;
- % defaults.realign.write.interp = 4;
- % defaults.realign.write.wrap = [0 0 0];
- % defaults.realign.write.which = [2 1];
- %
- % % Unwarp defaults
- % %==========================================================================
- % defaults.unwarp.estimate.rtm = 0;
- % defaults.unwarp.estimate.fwhm = 4;
- % defaults.unwarp.estimate.basfcn = [12 12];
- % defaults.unwarp.estimate.regorder = 1;
- % defaults.unwarp.estimate.regwgt = 1e5;
- % defaults.unwarp.estimate.foe = [4 5];
- % defaults.unwarp.estimate.soe = [];
- % defaults.unwarp.estimate.rem = 1;
- % defaults.unwarp.estimate.jm = 0;
- % defaults.unwarp.estimate.noi = 5;
- % defaults.unwarp.estimate.expround = 'Average';
- % %
- % % Unwarp uses defaults.realign.write defaults for writing.
- % %
- %
- % % Coregistration defaults
- % %==========================================================================
- % defaults.coreg.estimate.cost_fun = 'nmi';
- % defaults.coreg.estimate.sep = [4 2];
- % defaults.coreg.estimate.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];
- % defaults.coreg.estimate.fwhm = [7 7];
- % defaults.coreg.write.interp = 4;
- % defaults.coreg.write.wrap = [0 0 0];
- % defaults.coreg.write.mask = 0;
- %
- % % Spatial Normalisation defaults
- % %==========================================================================
- % defaults.normalise.write.preserve = 0;
- % defaults.normalise.write.bb = [[-78 -112 -70];[78 76 85]];
- % defaults.normalise.write.vox = [2 2 2];
- % defaults.normalise.write.interp = 4;
- %
- % % Old Spatial Normalisation defaults
- % %==========================================================================
- % defaults.old.normalise.estimate.smosrc = 8;
- % defaults.old.normalise.estimate.smoref = 0;
- % defaults.old.normalise.estimate.regtype = 'mni';
- % defaults.old.normalise.estimate.weight = '';
- % defaults.old.normalise.estimate.cutoff = 25;
- % defaults.old.normalise.estimate.nits = 16;
- % defaults.old.normalise.estimate.reg = 1;
- % defaults.old.normalise.write.preserve = 0;
- % defaults.old.normalise.write.bb = [[-78 -112 -70];[78 76 85]];
- % defaults.old.normalise.write.vox = [2 2 2];
- % defaults.old.normalise.write.interp = 1;
- % defaults.old.normalise.write.wrap = [0 0 0];
- % defaults.old.normalise.write.prefix = 'w';
- %
- % % VBM Preprocessing defaults
- % %==========================================================================
- % defaults.old.preproc.tpm = cellstr(char(...
- % fullfile(spm('Dir'),'toolbox','OldSeg','grey.nii'),...
- % fullfile(spm('Dir'),'toolbox','OldSeg','white.nii'),...
- % fullfile(spm('Dir'),'toolbox','OldSeg','csf.nii'))); % Prior probability maps
- % defaults.old.preproc.ngaus = [2 2 2 4]'; % Gaussians per class
- % defaults.old.preproc.warpreg = 1; % Warping Regularisation
- % defaults.old.preproc.warpco = 25; % Warp Frequency Cutoff
- % defaults.old.preproc.biasreg = 0.0001; % Bias regularisation
- % defaults.old.preproc.biasfwhm = 60; % Bias FWHM
- % defaults.old.preproc.regtype = 'mni'; % Affine Regularisation
- % defaults.old.preproc.fudge = 5; % Fudge factor, can not be set in batch GUI
- % defaults.old.preproc.samp = 3; % Sampling distance
- % defaults.old.preproc.output.GM = [0 0 1];
- % defaults.old.preproc.output.WM = [0 0 1];
- % defaults.old.preproc.output.CSF = [0 0 0];
- % defaults.old.preproc.output.biascor = 1;
- % defaults.old.preproc.output.cleanup = 0;
- %
- % % Smooth defaults
- % %==========================================================================
- % defaults.smooth.fwhm = [8 8 8];
diff --git a/src/QA/anatomicalQA.m b/src/QA/anatomicalQA.m
new file mode 100644
index 00000000..4016af5a
--- /dev/null
+++ b/src/QA/anatomicalQA.m
@@ -0,0 +1,51 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function anatomicalQA(opt)
+ % anatomicalQA(opt)
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ [group, opt, BIDS] = getData(opt);
+
+ fprintf(1, ' ANATOMICAL: QUALITY CONTROL\n\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};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+
+ % get grey and white matter tissue probability maps
+ TPMs = validationInputFile(anatDataDir, anatImage, 'c[12]');
+
+ % sanity check that all images are in the same space.
+ anatImage = fullfile(anatDataDir, anatImage);
+ volumesToCheck = {anatImage; TPMs(1, :); TPMs(2, :)};
+ spm_check_orientations(spm_vol(char(volumesToCheck)));
+
+ % Basic QA for anatomical data is to get SNR, CNR, FBER and Entropy
+ % This is useful to check coregistration worked fine
+ anatQA = spmup_anatQA(anatImage, TPMs(1, :), TPMs(2, :)); %#ok<*NASGU>
+
+ anatQA.avgDistToSurf = spmup_comp_dist2surf(fullfile(anatDataDir, anatImage));
+
+ spm_jsonwrite( ...
+ strrep(anatImage, '.nii', '_qa.json'), ...
+ anatQA, ...
+ struct('indent', ' '));
+
+ end
+ end
+
+end
diff --git a/src/QA/functionalQA.m b/src/QA/functionalQA.m
new file mode 100644
index 00000000..823e7ac5
--- /dev/null
+++ b/src/QA/functionalQA.m
@@ -0,0 +1,127 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function functionalQA(opt)
+ % functionalQA(opt)
+ %
+ % For functional data, QA is consists in getting temporal SNR and then
+ % check for motion - here we also compute additional regressors to
+ % account for motion
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ [group, opt, BIDS] = getData(opt);
+
+ fprintf(1, ' FUNCTIONAL: QUALITY CONTROL\n\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};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ % get grey and white matter and csf tissue probability maps
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+ TPMs = validationInputFile(anatDataDir, anatImage, 'rc[123]');
+
+ % load metrics from anat QA
+ anatQA = spm_jsonread( ...
+ fullfile( ...
+ anatDataDir, ...
+ strrep(anatImage, '.nii', '_qa.json')));
+
+ [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
+
+ for iSes = 1:nbSessions
+
+ % get all runs for that subject across all sessions
+ [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ for iRun = 1:nbRuns
+
+ % get the filename for this bold run for this task
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, ...
+ sessions{iSes}, ...
+ runs{iRun}, ...
+ opt);
+
+ prefix = getPrefix('smoothing_space-individual', opt);
+ funcImage = validationInputFile(subFuncDataDir, fileName, prefix);
+
+ % sanity check that all images are in the same space.
+ volumesToCheck = {funcImage; TPMs(1, :); TPMs(2, :); TPMs(3, :)};
+ spm_check_orientations(spm_vol(char(volumesToCheck)));
+
+ fMRIQA = computeFuncQAMetrics(funcImage, TPMs, anatQA.avgDistToSurf, opt);
+
+ spm_jsonwrite( ...
+ fullfile( ...
+ subFuncDataDir, ...
+ strrep(fileName, '.nii', '_qa.json')), ...
+ fMRIQA, ...
+ struct('indent', ' '));
+
+ outputFiles = spmup_first_level_qa( ...
+ funcImage, ...
+ 'Voltera', 'on', ...
+ 'Radius', anatQA.avgDistToSurf);
+
+ movefile( ...
+ fullfile(subFuncDataDir, 'spmup_QC.ps'), ...
+ fullfile(subFuncDataDir, strrep(fileName, '.nii', '_qa.ps')));
+
+ confounds = load(outputFiles.design);
+
+ spm_save( ...
+ fullfile( ...
+ subFuncDataDir, ...
+ strrep(fileName, ...
+ '_bold.nii', ...
+ '_desc-confounds_regressors.tsv')), ...
+ confounds);
+
+ delete(outputFiles.design);
+
+ createDataDictionary(subFuncDataDir, fileName, size(confounds, 2));
+
+ % create carpet plot
+ % spmup_timeseriesplot(P, c1, c2, c3, ...
+ % 'motion', 'on', ...
+ % 'nuisances', 'on', ...
+ % 'correlation', 'on');
+
+ end
+
+ end
+
+ end
+
+ end
+
+end
+
+function fMRIQA = computeFuncQAMetrics(funcImage, TPMs, avgDistToSurf, opt)
+
+ [subFuncDataDir, fileName, ext] = spm_fileparts(funcImage);
+
+ prefix = getPrefix('smoothing_space-individual', opt);
+
+ fMRIQA.tSNR = spmup_temporalSNR( ...
+ funcImage, ...
+ {TPMs(1, :); TPMs(2, :); TPMs(3, :)}, ...
+ 'save');
+
+ realignParamFile = getRealignParamFile(fullfile(subFuncDataDir, [fileName, ext]), prefix);
+ fMRIQA.meanFD = mean(spmup_FD(realignParamFile, avgDistToSurf));
+
+end
diff --git a/src/batches/setBatchComputeVDM.m b/src/batches/setBatchComputeVDM.m
new file mode 100644
index 00000000..b464a2e0
--- /dev/null
+++ b/src/batches/setBatchComputeVDM.m
@@ -0,0 +1,65 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchComputeVDM(matlabbatch, fmapType, refImage)
+ % 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
+
+ UF = struct( ...
+ 'method', 'Mark3D', ...
+ 'fwhm', 10, ...
+ 'pad', 0, ...
+ 'ws', 1);
+
+ MF = struct( ...
+ 'fwhm', 5, ...
+ 'nerode', 2, ...
+ 'ndilate', 4, ...
+ 'thresh', 0.5, ...
+ 'reg', 0.02);
+
+ defaultsval = struct( ...
+ 'et', [NaN NaN], ...
+ 'maskbrain', 1, ...
+ 'blipdir', 1, ...
+ 'tert', '', ...
+ 'epifm', 0, ... % can be changed
+ 'ajm', 0, ...
+ '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 = {refImage};
+ matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj(1).matchvdm = 1;
+ 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;
+
+end
diff --git a/src/batches/setBatchCoregistration.m b/src/batches/setBatchCoregistration.m
new file mode 100644
index 00000000..9562319e
--- /dev/null
+++ b/src/batches/setBatchCoregistration.m
@@ -0,0 +1,14 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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
new file mode 100644
index 00000000..98a913cb
--- /dev/null
+++ b/src/batches/setBatchCoregistrationFmap.m
@@ -0,0 +1,53 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchCoregistrationFmap(BIDS, opt, subID)
+
+ % TODO
+ % 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 ?
+ [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 = [];
+
+ 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 = setBatchCoregistration(matlabbatch, refImage, srcImage, otherImages);
+
+ end
+
+ end
+
+end
diff --git a/src/batches/setBatchCoregistrationFuncToAnat.m b/src/batches/setBatchCoregistrationFuncToAnat.m
new file mode 100644
index 00000000..02984f87
--- /dev/null
+++ b/src/batches/setBatchCoregistrationFuncToAnat.m
@@ -0,0 +1,76 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+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/setBatchCreateVDMs.m b/src/batches/setBatchCreateVDMs.m
new file mode 100644
index 00000000..2d3e7fd8
--- /dev/null
+++ b/src/batches/setBatchCreateVDMs.m
@@ -0,0 +1,119 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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'
+
+ 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
+
+ runs = spm_BIDS(BIDS, 'runs', ...
+ 'modality', 'fmap', ...
+ 'sub', subID, ...
+ 'ses', sessions{iSes});
+
+ for iRun = 1:numel(runs)
+
+ matlabbatch = setBatchComputeVDM(matlabbatch, 'phasediff', refImage);
+
+ % 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};
+
+ [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;
+ matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.blipdir = blipDir;
+ matlabbatch{end}.spm.tools.fieldmap.calculatevdm.subj.defaults.defaultsval.epifm = isEPI;
+
+ end
+
+ end
+
+end
+
+function varargout = getMetadataForVDM(BIDS, subID, sessionID, runID)
+
+ % 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(fmapMetadata);
+
+ isEPI = checkFmapPulseSequenceType(fmapMetadata);
+
+ varargout{1} = echotimes;
+ varargout{2} = isEPI;
+
+ [totalReadoutTime, blipDir] = getMetadataFromIntendedForFunc(BIDS, fmapMetadata);
+
+ varargout{3} = totalReadoutTime;
+ varargout{4} = blipDir;
+
+end
+
+function echotimes = getEchoTimes(fmapMetadata)
+
+ echotimes = 1000 * [ ...
+ fmapMetadata.EchoTime1, ...
+ fmapMetadata.EchoTime2]; % in milliseconds
+
+end
+
+function isEPI = checkFmapPulseSequenceType(fmapMetadata)
+
+ isEPI = 0;
+
+ if isfield(fmapMetadata, 'PulseSequenceType') && ...
+ sum(strfind(fmapMetadata.PulseSequenceType, 'EPI')) ~= 0
+
+ isEPI = 1;
+ end
+
+end
diff --git a/src/batches/setBatchFactorialDesign.m b/src/batches/setBatchFactorialDesign.m
new file mode 100644
index 00000000..4c26f327
--- /dev/null
+++ b/src/batches/setBatchFactorialDesign.m
@@ -0,0 +1,87 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchFactorialDesign(grpLvlCon, group, conFWHM, rfxDir)
+
+ fprintf(1, 'BUILDING JOB: Factorial Design Specification');
+
+ % Check which level of CON smoothing is desired
+ smoothPrefix = '';
+ if conFWHM > 0
+ smoothPrefix = ['s', num2str(conFWHM)];
+ end
+
+ con = 0;
+
+ % For each contrast
+ for j = 1:size(grpLvlCon, 1)
+
+ % the strrep(Session{j}, 'trial_type.', '') is there to remove
+ % 'trial_type.' because contrasts against baseline are renamed
+ % at the subject level
+ conName = rmTrialTypeStr(grpLvlCon{j});
+
+ con = con + 1;
+
+ % For each group
+ for iGroup = 1:length(group)
+
+ groupName = group(iGroup).name;
+
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.icell(iGroup).levels = ...
+ iGroup; %#ok<*AGROW>
+
+ for iSub = 1:group(iGroup).numSub
+
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ % FFX directory and load SPM.mat of that subject
+ ffxDir = getFFXdir(subID, funcFWHM, opt);
+ load(fullfile(ffxDir, 'SPM.mat'));
+
+ % find which contrast of that subject has the name of the contrast we
+ % want to bring to the group level
+ conIdx = find(strcmp({SPM.xCon.name}, conName));
+ fileName = sprintf('con_%0.4d.nii', conIdx);
+ file = validationInputFile(ffxDir, fileName, smoothPrefix);
+
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.icell(iGroup).scans(iSub, :) = ...
+ {file};
+
+ end
+
+ end
+
+ % GROUP and the number of levels in the group. if 2 groups ,
+ % then number of levels = 2
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.name = 'GROUP';
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.levels = 1;
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.dept = 0;
+
+ % 1: Assumes that the variance is not the same across groups
+ % 0: There is no difference in the variance between groups
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.variance = 1;
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.gmsca = 0;
+ matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.ancova = 0;
+ % matlabbatch{j}.spm.stats.factorial_design.cov = [];
+ matlabbatch{j}.spm.stats.factorial_design.masking.tm.tm_none = 1;
+ matlabbatch{j}.spm.stats.factorial_design.masking.im = 1;
+ matlabbatch{j}.spm.stats.factorial_design.masking.em = { ...
+ fullfile(rfxDir, 'MeanMask.nii')};
+ matlabbatch{j}.spm.stats.factorial_design.globalc.g_omit = 1;
+ matlabbatch{j}.spm.stats.factorial_design.globalm.gmsca.gmsca_no = 1;
+ matlabbatch{j}.spm.stats.factorial_design.globalm.glonorm = 1;
+
+ % If it exists, issue a warning that it has been overwritten
+ if exist(fullfile(rfxDir, conName), 'dir')
+ warning('overwriting directory: %s \n', fullfile(rfxDir, conName));
+ rmdir(fullfile(rfxDir, conName), 's');
+ end
+
+ mkdir(fullfile(rfxDir, conName));
+ matlabbatch{j}.spm.stats.factorial_design.dir = { fullfile(rfxDir, conName) };
+
+ end
+
+end
diff --git a/src/batches/setBatchImageCalculation.m b/src/batches/setBatchImageCalculation.m
new file mode 100644
index 00000000..90923d33
--- /dev/null
+++ b/src/batches/setBatchImageCalculation.m
@@ -0,0 +1,16 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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
diff --git a/src/batches/setBatchMeanAnatAndMask.m b/src/batches/setBatchMeanAnatAndMask.m
new file mode 100644
index 00000000..bc4a02ce
--- /dev/null
+++ b/src/batches/setBatchMeanAnatAndMask.m
@@ -0,0 +1,81 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchMeanAnatAndMask(opt, funcFWHM, rfxDir)
+
+ [group, opt, BIDS] = getData(opt);
+
+ matlabbatch = {};
+
+ % Create Mean Structural Image
+ fprintf(1, 'BUILDING JOB: Create Mean Structural Image...');
+
+ subCounter = 0;
+
+ for iGroup = 1:length(group)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ subCounter = subCounter + 1;
+
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ %% STRUCTURAL
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+
+ anatImage = validationInputFile( ...
+ anatDataDir, ...
+ anatImage, ...
+ [spm_get_defaults('normalise.write.prefix'), ...
+ spm_get_defaults('deformations.modulate.prefix')]);
+
+ matlabbatch{1}.spm.util.imcalc.input{subCounter, :} = anatImage;
+
+ %% Mask
+ ffxDir = getFFXdir(subID, funcFWHM, opt);
+
+ files = validationInputFile(ffxDir, 'mask.nii');
+
+ matlabbatch{2}.spm.util.imcalc.input{subCounter, :} = files;
+
+ end
+ end
+
+ %% Generate the equation to get the mean of the mask and structural image
+ % example : if we have 5 subjects, Average equation = '(i1+i2+i3+i4+i5)/5'
+ nbImg = subCounter;
+ imgRange = 1:subCounter;
+
+ tmpImg = sprintf('+i%i', imgRange);
+ tmpImg = tmpImg(2:end);
+ sumEquation = ['(', tmpImg, ')'];
+
+ % meanStruct_equation = '(i1+i2+i3+i4+i5)/5'
+ meanStruct_equation = ['(', tmpImg, ')/', num2str(nbImg)];
+
+ % ------
+ % TODO
+ % not sure this makes sense for the mask as voxels that have no data for one
+ % subject are excluded anyway !!!!
+
+ % meanMask_equation = '(i1+i2+i3+i4+i5)>0.75*5'
+ meanMask_equation = strcat(sumEquation, '>0.75*', num2str(nbImg));
+
+ %% The mean structural will be saved in the RFX folder
+ matlabbatch{1}.spm.util.imcalc.output = 'meanAnat.nii';
+ matlabbatch{1}.spm.util.imcalc.outdir{:} = rfxDir;
+ matlabbatch{1}.spm.util.imcalc.expression = meanStruct_equation;
+ % matlabbatch{1}.spm.util.imcalc.options.interp = 1;
+ % matlabbatch{1}.spm.util.imcalc.options.dtype = 4;
+
+ %% The mean mask will be saved in the RFX folder
+ matlabbatch{2}.spm.util.imcalc.output = 'meanMask.nii';
+ matlabbatch{2}.spm.util.imcalc.outdir{:} = rfxDir;
+ matlabbatch{2}.spm.util.imcalc.expression = meanMask_equation;
+ % matlabbatch{2}.spm.util.imcalc.options.interp = 1;
+ % matlabbatch{2}.spm.util.imcalc.options.dtype = 4;
+
+end
diff --git a/src/batches/setBatchNormalizationSpatialPrepro.m b/src/batches/setBatchNormalizationSpatialPrepro.m
new file mode 100644
index 00000000..6c79ab42
--- /dev/null
+++ b/src/batches/setBatchNormalizationSpatialPrepro.m
@@ -0,0 +1,85 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchNormalizationSpatialPrepro(matlabbatch, voxDim, opt)
+
+ fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE FUNCTIONALS\n');
+
+ jobsToAdd = numel(matlabbatch) + 1;
+
+ for iJob = jobsToAdd:(jobsToAdd + 4)
+
+ % set the deformation field for all the images we are about to normalize
+ deformationField = ...
+ cfg_dep('Segment: Forward Deformations', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'fordef', '()', {':'}));
+
+ % we set images to be resampled at the voxel size we had at acquisition
+ matlabbatch = setBatchNormalize(matlabbatch, deformationField, voxDim);
+
+ end
+
+ fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE FUNCIONAL\n');
+ matlabbatch{jobsToAdd}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Coregister: Estimate: Coregistered Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.coregister}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'cfiles'));
+
+ % NORMALIZE STRUCTURAL
+ fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE STRUCTURAL\n');
+ matlabbatch{jobsToAdd + 1}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: Bias Corrected (1)', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'channel', '()', {1}, ...
+ '.', 'biascorr', '()', {':'}));
+ % size 3 allow to run RunQA / original voxel size at acquisition
+ matlabbatch{jobsToAdd + 1}.spm.spatial.normalise.write.woptions.vox = [1 1 1];
+
+ % NORMALIZE GREY MATTER
+ fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE GREY MATTER\n');
+ matlabbatch{jobsToAdd + 2}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: c1 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {1}, ...
+ '.', 'c', '()', {':'}));
+
+ % NORMALIZE WHITE MATTER
+ fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE WHITE MATTER\n');
+ matlabbatch{jobsToAdd + 3}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: c2 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {2}, ...
+ '.', 'c', '()', {':'}));
+
+ % NORMALIZE CSF MATTER
+ fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE CSF\n');
+ matlabbatch{jobsToAdd + 4}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: c3 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {3}, ...
+ '.', 'c', '()', {':'}));
+
+end
diff --git a/src/batches/setBatchNormalize.m b/src/batches/setBatchNormalize.m
new file mode 100644
index 00000000..831c0682
--- /dev/null
+++ b/src/batches/setBatchNormalize.m
@@ -0,0 +1,25 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchNormalize(matlabbatch, deformField, voxDim, imgToResample)
+
+ if nargin > 1 && ~isempty(deformField)
+ matlabbatch{end + 1}.spm.spatial.normalise.write.subj.def(1) = deformField;
+ end
+
+ if nargin > 2 && ~isempty(voxDim)
+ matlabbatch{end}.spm.spatial.normalise.write.woptions.vox = voxDim;
+ end
+
+ if nargin > 3 && ~isempty(imgToResample)
+ matlabbatch{end}.spm.spatial.normalise.write.subj.resample(1) = imgToResample;
+ end
+
+ % The following lines are commented out because those parameters
+ % can be set in the spm_my_defaults.m
+ % matlabbatch{iJob}.spm.spatial.normalise.write.woptions.bb = ...
+ % [-78 -112 -70 ; 78 76 85];
+ % matlabbatch{iJob}.spm.spatial.normalise.write.woptions.interp = 4;
+ % matlabbatch{iJob}.spm.spatial.normalise.write.woptions.prefix = ...
+ % spm_get_defaults('normalise.write.prefix');
+
+end
diff --git a/src/batches/setBatchRealign.m b/src/batches/setBatchRealign.m
new file mode 100644
index 00000000..172535a2
--- /dev/null
+++ b/src/batches/setBatchRealign.m
@@ -0,0 +1,152 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function [matlabbatch, voxDim] = setBatchRealign(varargin)
+ %
+ % Set the batch for realign / realign and reslice / realign and unwarp
+ %
+ % 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)
+
+ if numel(varargin) < 5
+ [matlabbatch, BIDS, subID, opt] = deal(varargin{:});
+ action = '';
+ else
+ [matlabbatch, BIDS, subID, opt, action] = deal(varargin{:});
+ end
+
+ if isempty(action)
+ action = 'realign';
+ end
+
+ % TODO hide this wart in a subfunction ?
+ switch action
+ case 'realignUnwarp'
+ msg = ' & UNWARP';
+ matlabbatch{end + 1}.spm.spatial.realignunwarp.eoptions.weight = {''};
+ matlabbatch{end}.spm.spatial.realignunwarp.uwroptions.uwwhich = [2 1];
+
+ case 'realignReslice'
+ msg = ' & RESLICE';
+ matlabbatch{end + 1}.spm.spatial.realign.estwrite.eoptions.weight = {''};
+ matlabbatch{1}.spm.spatial.realign.estwrite.roptions.which = [2 1];
+
+ case 'realign'
+ msg = [];
+ matlabbatch{end + 1}.spm.spatial.realign.estwrite.eoptions.weight = {''};
+ matlabbatch{end}.spm.spatial.realign.estwrite.roptions.which = [0 1];
+
+ end
+
+ fprintf(1, ' BUILDING SPATIAL JOB : REALIGN%s\n', msg);
+
+ [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
+
+ runCounter = 1;
+
+ for iSes = 1:nbSessions
+
+ % get all runs for that subject across all sessions
+ [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ for iRun = 1:nbRuns
+
+ % get the filename for this bold run for this task
+ [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, boldFilename, prefix);
+ [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, boldFilename);
+
+ if size(file, 1) > 1
+ errorStruct.identifier = 'setBatchRealign:tooManyFiles';
+ errorStruct.message = 'This should only get on file.';
+ error(errorStruct);
+ end
+
+ fprintf(1, ' %s\n', file);
+
+ if strcmp(action, 'realignUnwarp')
+
+ 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;
+ end
+
+ end
+
+ %% defaults
+ % The following lines are commented out because those parameters
+ % can be set in the spm_my_defaults.m
+
+ % -----------------
+ % REALIGN
+ % REALIGN & RESLICE
+ % -----------------
+ %
+ % matlabbatch{end}.spm.spatial.realign.estwrite.eoptions.quality = 1;
+ % matlabbatch{end}.spm.spatial.realign.estwrite.eoptions.sep = 2;
+ % matlabbatch{end}.spm.spatial.realign.estwrite.eoptions.fwhm = 5;
+ % matlabbatch{end}.spm.spatial.realign.estwrite.eoptions.rtm = 0;
+ % matlabbatch{end}.spm.spatial.realign.estwrite.eoptions.interp = 2;
+ % matlabbatch{end}.spm.spatial.realign.estwrite.eoptions.wrap = [0 0 0];
+ %
+ % matlabbatch{end}.spm.spatial.realign.estwrite.roptions.interp = 3;
+ % matlabbatch{end}.spm.spatial.realign.estwrite.roptions.wrap = [0 0 0];
+ % matlabbatch{end}.spm.spatial.realign.estwrite.roptions.mask = 1;
+
+ % ----------------
+ % REALIGN & UNWARP
+ % ----------------
+ % matlabbatch{1}.spm.spatial.realignunwarp.eoptions.quality = 1;
+ % matlabbatch{1}.spm.spatial.realignunwarp.eoptions.sep = 2;
+ % matlabbatch{1}.spm.spatial.realignunwarp.eoptions.fwhm = 5;
+ % matlabbatch{1}.spm.spatial.realignunwarp.eoptions.rtm = 0;
+ % matlabbatch{1}.spm.spatial.realignunwarp.eoptions.einterp = 2;
+ % matlabbatch{1}.spm.spatial.realignunwarp.eoptions.ewrap = [0 0 0];
+ %
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.basfcn = [12 12];
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.regorder = 1;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.lambda = 100000;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.jm = 0;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.fot = [4 5];
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.sot = [];
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.uwfwhm = 4;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.rem = 1;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.noi = 5;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uweoptions.expround = 'Average';
+ %
+ % matlabbatch{1}.spm.spatial.realignunwarp.uwroptions.rinterp = 3;
+ % matlabbatch{1}.spm.spatial.realignunwarp.uwroptions.wrap = [0 0 0];
+ % matlabbatch{1}.spm.spatial.realignunwarp.uwroptions.mask = 1;
+
+end
diff --git a/src/batches/setBatchReslice.m b/src/batches/setBatchReslice.m
new file mode 100644
index 00000000..2c103b3f
--- /dev/null
+++ b/src/batches/setBatchReslice.m
@@ -0,0 +1,14 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchReslice(referenceImg, sourceImages)
+ % matlabbatch = bidsSmoothing(referenceImg, sourceImages)
+ %
+ % referenceImg
+ %
+ % sourceImages: a cell
+
+ matlabbatch = [];
+ matlabbatch{1}.spm.spatial.coreg.write.ref = {referenceImg};
+ matlabbatch{1}.spm.spatial.coreg.write.source = sourceImages;
+
+end
diff --git a/src/batches/setBatchSTC.m b/src/batches/setBatchSTC.m
new file mode 100644
index 00000000..a0335a53
--- /dev/null
+++ b/src/batches/setBatchSTC.m
@@ -0,0 +1,99 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSTC(BIDS, opt, subID)
+ % matlabbatch = setBatchSTC(BIDS, opt, subID)
+ %
+ % Slice timing units is in milliseconds to be BIDS compliant and not in slice number
+ % as is more traditionally the case with SPM.
+ %
+ % In the case the slice timing information was not specified in the json FILES
+ % in the BIDS data set (e.g it couldnt be extracted from the trento old scanner),
+ % then add this information manually in opt.sliceOrder field.
+ %
+ % If this is empty the slice timing correction will not be performed
+ %
+ % If not specified this function will take the mid-volume time point as reference
+ % to do the slice timing correction
+ %
+ % See README.md for more information about slice timing correction
+
+ matlabbatch = [];
+
+ % get slice order
+ sliceOrder = getSliceOrder(opt, 1);
+ if isempty(sliceOrder)
+ warning('No slice order dectected: skipping slice timing correction.');
+ return
+ end
+
+ fprintf(1, ' BUILDING STC JOB : STC\n');
+
+ % get metadata for STC
+ % Note that slice ordering is assumed to be from foot to head. If it is not, enter
+ % instead: TR - INTRASCAN_TIME - SLICE_TIMING_VECTOR
+
+ % SPM accepts slice time acquisition as inputs for slice order (simplifies
+ % things when dealing with multiecho data)
+ nbSlices = length(sliceOrder); % unique is necessary in case of multi echo
+ TR = opt.metadata.RepetitionTime;
+ TA = TR - (TR / nbSlices);
+
+ maxSliceTime = max(sliceOrder);
+ minSliceTime = min(sliceOrder);
+ if isempty(opt.STC_referenceSlice)
+ referenceSlice = (maxSliceTime - minSliceTime) / 2;
+ else
+ referenceSlice = opt.STC_referenceSlice;
+ end
+ if referenceSlice > TA
+ error('%s (%f) %s (%f).\n%s', ...
+ 'The reference slice time', referenceSlice, ...
+ 'is greater than the acquisition time', TA, ...
+ ['Reference slice time must be in milliseconds ' ...
+ 'or leave it empty to use mid-acquisition time as reference.']);
+ end
+
+ % prefix of the files to look for
+ prefix = getPrefix('STC', opt);
+
+ [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
+
+ runCounter = 1;
+
+ for iSes = 1:nbSessions
+
+ % get all runs for that subject for this session
+ [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ for iRun = 1:nbRuns
+
+ % get the filename for this bold run for this task
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{iSes}, runs{iRun}, opt);
+
+ % check that the file with the right prefix exist
+ file = validationInputFile(subFuncDataDir, prefix, fileName);
+
+ % add the file to the list
+ matlabbatch{1}.spm.temporal.st.scans{runCounter} = {file};
+
+ runCounter = runCounter + 1;
+
+ disp(file);
+
+ end
+
+ end
+
+ matlabbatch{1}.spm.temporal.st.nslices = nbSlices;
+ matlabbatch{1}.spm.temporal.st.tr = TR;
+ matlabbatch{1}.spm.temporal.st.ta = TA;
+ matlabbatch{1}.spm.temporal.st.so = sliceOrder;
+ matlabbatch{1}.spm.temporal.st.refslice = referenceSlice;
+
+ % The following lines are commented out because those parameters
+ % can be set in the spm_my_defaults.m
+ % matlabbatch{1}.spm.temporal.st.prefix = spm_get_defaults('slicetiming.prefix');
+
+end
diff --git a/src/batches/setBatchSaveCoregistrationMatrix.m b/src/batches/setBatchSaveCoregistrationMatrix.m
new file mode 100644
index 00000000..ac5d5008
--- /dev/null
+++ b/src/batches/setBatchSaveCoregistrationMatrix.m
@@ -0,0 +1,31 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSaveCoregistrationMatrix(matlabbatch, BIDS, subID, opt)
+
+ % create name of the output file based on the name of the first image of the
+ % first session
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
+ runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1});
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{1}, runs{1}, opt);
+
+ fileName = strrep(fileName, '_bold.nii', '_from-scanner_to-T1w_mode-image_xfm.mat');
+
+ matlabbatch{end + 1}.cfg_basicio.var_ops.cfg_save_vars.name = fileName;
+ matlabbatch{end}.cfg_basicio.var_ops.cfg_save_vars.outdir = {subFuncDataDir};
+ matlabbatch{end}.cfg_basicio.var_ops.cfg_save_vars.vars.vname = 'transformationMatrix';
+
+ matlabbatch{end}.cfg_basicio.var_ops.cfg_save_vars.vars.vcont(1) = ...
+ cfg_dep( ...
+ 'Coregister: Estimate: Coregistration Matrix', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.coregister}, ...
+ '.', 'val', '{}', {opt.orderBatches.coregister}, ...
+ '.', 'val', '{}', {opt.orderBatches.coregister}, ...
+ '.', 'val', '{}', {opt.orderBatches.coregister}), ...
+ substruct('.', 'M'));
+
+ matlabbatch{end}.cfg_basicio.var_ops.cfg_save_vars.saveasstruct = false;
+
+end
diff --git a/src/batches/setBatchSegmentation.m b/src/batches/setBatchSegmentation.m
new file mode 100644
index 00000000..78721948
--- /dev/null
+++ b/src/batches/setBatchSegmentation.m
@@ -0,0 +1,71 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSegmentation(matlabbatch, opt)
+
+ fprintf(1, ' BUILDING SPATIAL JOB : SEGMENT ANATOMICAL\n');
+
+ % define SPM folder
+ spmLocation = spm('dir');
+
+ % SAVE BIAS CORRECTED IMAGE
+ matlabbatch{end + 1}.spm.spatial.preproc.channel.vols(1) = ...
+ cfg_dep('Named File Selector: Anatomical(1) - Files', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.selectAnat}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'files', '{}', {1}));
+
+ matlabbatch{end}.spm.spatial.preproc.channel.biasreg = 0.001;
+ matlabbatch{end}.spm.spatial.preproc.channel.biasfwhm = 60;
+ % save bias correction field = false
+ % save bias corrected image = true
+ matlabbatch{end}.spm.spatial.preproc.channel.write = [false true];
+
+ % CREATE SEGMENTS IN NATIVE SPACE OF GM,WM AND CSF
+ matlabbatch{end}.spm.spatial.preproc.tissue(1).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',1']};
+ matlabbatch{end}.spm.spatial.preproc.tissue(1).ngaus = 1;
+ matlabbatch{end}.spm.spatial.preproc.tissue(1).native = [1 1];
+ matlabbatch{end}.spm.spatial.preproc.tissue(1).warped = [0 0];
+
+ matlabbatch{end}.spm.spatial.preproc.tissue(2).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',2']};
+ matlabbatch{end}.spm.spatial.preproc.tissue(2).ngaus = 1;
+ matlabbatch{end}.spm.spatial.preproc.tissue(2).native = [1 1];
+ matlabbatch{end}.spm.spatial.preproc.tissue(2).warped = [0 0];
+
+ matlabbatch{end}.spm.spatial.preproc.tissue(3).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',3']};
+ matlabbatch{end}.spm.spatial.preproc.tissue(3).ngaus = 2;
+ matlabbatch{end}.spm.spatial.preproc.tissue(3).native = [1 1];
+ matlabbatch{end}.spm.spatial.preproc.tissue(3).warped = [0 0];
+
+ matlabbatch{end}.spm.spatial.preproc.tissue(4).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',4']};
+ matlabbatch{end}.spm.spatial.preproc.tissue(4).ngaus = 3;
+ matlabbatch{end}.spm.spatial.preproc.tissue(4).native = [0 0];
+ matlabbatch{end}.spm.spatial.preproc.tissue(4).warped = [0 0];
+
+ matlabbatch{end}.spm.spatial.preproc.tissue(5).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',5']};
+ matlabbatch{end}.spm.spatial.preproc.tissue(5).ngaus = 4;
+ matlabbatch{end}.spm.spatial.preproc.tissue(5).native = [0 0];
+ matlabbatch{end}.spm.spatial.preproc.tissue(5).warped = [0 0];
+
+ matlabbatch{end}.spm.spatial.preproc.tissue(6).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',6']};
+ matlabbatch{end}.spm.spatial.preproc.tissue(6).ngaus = 2;
+ matlabbatch{end}.spm.spatial.preproc.tissue(6).native = [0 0];
+ matlabbatch{end}.spm.spatial.preproc.tissue(6).warped = [0 0];
+
+ % SAVE FORWARD DEFORMATION FIELD FOR NORMALISATION
+ matlabbatch{end}.spm.spatial.preproc.warp.mrf = 1;
+ matlabbatch{end}.spm.spatial.preproc.warp.cleanup = 1;
+ matlabbatch{end}.spm.spatial.preproc.warp.reg = [0 0.001 0.5 0.05 0.2];
+ matlabbatch{end}.spm.spatial.preproc.warp.affreg = 'mni';
+ matlabbatch{end}.spm.spatial.preproc.warp.fwhm = 0;
+ matlabbatch{end}.spm.spatial.preproc.warp.samp = 3;
+ matlabbatch{end}.spm.spatial.preproc.warp.write = [1 1];
+end
diff --git a/src/batches/setBatchSelectAnat.m b/src/batches/setBatchSelectAnat.m
new file mode 100644
index 00000000..d4476f08
--- /dev/null
+++ b/src/batches/setBatchSelectAnat.m
@@ -0,0 +1,19 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subID)
+ % matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subID)
+ %
+ % Creates a batch to set an anat image
+ % - image type = opt.anatReference.type (default = T1w)
+ % - session to select the anat from = opt.anatReference.session (default = 1)
+ %
+ % We assume that the first anat of that type is the "correct" one
+
+ fprintf(1, ' BUILDING SPATIAL JOB : SELECTING ANATOMCAL\n');
+
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+
+ matlabbatch{end + 1}.cfg_basicio.cfg_named_file.name = 'Anatomical';
+ matlabbatch{end}.cfg_basicio.cfg_named_file.files = { {fullfile(anatDataDir, anatImage)} };
+
+end
diff --git a/src/batches/setBatchSkullStripping.m b/src/batches/setBatchSkullStripping.m
new file mode 100644
index 00000000..b69c1a00
--- /dev/null
+++ b/src/batches/setBatchSkullStripping.m
@@ -0,0 +1,119 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, subID, opt)
+ %
+ % Creates a batch to computes a brain mask based on the tissue probability maps
+ % from the segmentaion.
+ %
+ % USAGE::
+ %
+ % matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, subID, opt)
+ %
+ % :param matlabbatch: list of SPM batches
+ % :type matlabbatch: structure
+ % :param BIDS: dataset layout returned by getData
+ % :type BIDS: structure
+ % :param subID: subject ID
+ % :type subID: string
+ % :param opt: options
+ % :type opt: structure
+ %
+ % :returns: - :matlabbatch: (structure)
+ %
+ % This function will get its inputs from the segmentation batch by reading
+ % the dependency from ``opt.orderBatches.segment``. If this field is not specified it will
+ % try to get the results from the segmentation by relying on the ``anat``
+ % image returned by ``getAnatFilename``.
+ %
+ % The threshold for inclusion in the mask can be set by::
+ %
+ % opt.skullstrip.threshold
+ %
+ % The defaukt is ``0.75``.
+ %
+ % Any voxel with p(grayMatter) + p(whiteMatter) + p(CSF) > threshold
+ % will be included in the skull stripping mask.
+ %
+
+ fprintf(1, ' BUILDING SPATIAL JOB : SKULL STRIPPING\n');
+
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+
+ % if this is part of a pipeline we get the segmentation dependency to get
+ % the input from.
+ % Otherwise the files to process are stored in a cell
+ if isfield(opt, 'orderBatches') && isfield(opt.orderBatches, 'segment')
+
+ matlabbatch{end + 1}.spm.util.imcalc.input(1) = ...
+ cfg_dep( ...
+ 'Segment: Bias Corrected (1)', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'channel', '()', {1}, ...
+ '.', 'biascorr', '()', {':'}));
+ matlabbatch{end}.spm.util.imcalc.input(2) = ...
+ cfg_dep( ...
+ 'Segment: c1 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {1}, ...
+ '.', 'c', '()', {':'}));
+ matlabbatch{end}.spm.util.imcalc.input(3) = ...
+ cfg_dep( ...
+ 'Segment: c2 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {2}, ...
+ '.', 'c', '()', {':'}));
+ matlabbatch{end}.spm.util.imcalc.input(4) = ...
+ cfg_dep( ...
+ 'Segment: c3 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {opt.orderBatches.segment}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {3}, ...
+ '.', 'c', '()', {':'}));
+ else
+
+ % bias corrected image
+ biasCorrectedAnatImage = validationInputFile(anatDataDir, anatImage, 'm');
+ matlabbatch{end + 1}.spm.util.imcalc.input(1) = biasCorrectedAnatImage;
+
+ % get the tissue probability maps in native space for that subject
+ TPMs = validationInputFile(anatDataDir, anatImage, 'c[123]');
+
+ % grey matter
+ matlabbatch{end}.spm.util.imcalc.input(2) = TPMs(1, :);
+ % white matter
+ matlabbatch{end}.spm.util.imcalc.input(3) = TPMs(2, :);
+ % csf
+ matlabbatch{end}.spm.util.imcalc.input(4) = TPMs(3, :);
+
+ end
+
+ matlabbatch{end}.spm.util.imcalc.output = ['m' strrep(anatImage, '.nii', '_skullstripped.nii')];
+ matlabbatch{end}.spm.util.imcalc.outdir = {anatDataDir};
+
+ matlabbatch{end}.spm.util.imcalc.expression = sprintf( ...
+ 'i1.*((i2+i3+i4)>%f)', ...
+ opt.skullstrip.threshold);
+
+ % add a batch to output the mask
+ matlabbatch{end + 1} = matlabbatch{end};
+ matlabbatch{end}.spm.util.imcalc.expression = sprintf( ...
+ '(i2+i3+i4)>%f', ...
+ opt.skullstrip.threshold);
+ matlabbatch{end}.spm.util.imcalc.output = ['m' strrep(anatImage, '.nii', '_mask.nii')];
+
+end
diff --git a/src/batches/setBatchSmoothConImages.m b/src/batches/setBatchSmoothConImages.m
new file mode 100644
index 00000000..a6d65773
--- /dev/null
+++ b/src/batches/setBatchSmoothConImages.m
@@ -0,0 +1,40 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSmoothConImages(group, funcFWHM, conFWHM, opt)
+
+ fprintf(1, 'SMOOTHING CON IMAGES...');
+
+ counter = 0;
+
+ matlabbatch = {};
+
+ %% Loop through the groups, subjects, and sessions
+ for iGroup = 1:length(group)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ counter = counter + 1;
+
+ subNumber = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subNumber);
+
+ ffxDir = getFFXdir(subNumber, funcFWHM, opt);
+
+ conImg = spm_select('FPlist', ffxDir, '^con*.*nii$');
+ matlabbatch{counter}.spm.spatial.smooth.data = cellstr(conImg); %#ok<*AGROW>
+
+ % Define how much smoothing is required
+ matlabbatch{counter}.spm.spatial.smooth.fwhm = ...
+ [conFWHM conFWHM conFWHM];
+ matlabbatch{counter}.spm.spatial.smooth.dtype = 0;
+ matlabbatch{counter}.spm.spatial.smooth.prefix = [ ...
+ spm_get_defaults('smooth.prefix'), ...
+ num2str(conFWHM)];
+
+ end
+ end
+
+end
diff --git a/src/batches/setBatchSmoothing.m b/src/batches/setBatchSmoothing.m
new file mode 100644
index 00000000..1ca9015c
--- /dev/null
+++ b/src/batches/setBatchSmoothing.m
@@ -0,0 +1,56 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSmoothing(BIDS, opt, subID, funcFWHM)
+
+ fprintf(1, 'PREPARING: SMOOTHING JOB \n');
+
+ % creates prefix to look for
+ prefix = getPrefix('smoothing', opt);
+ if strcmp(opt.space, 'individual')
+ prefix = getPrefix('smoothing_space-individual', opt);
+ end
+
+ % identify sessions for this subject
+ [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
+
+ % clear previous matlabbatch and files
+ matlabbatch = [];
+ allFiles = [];
+
+ sesCounter = 1;
+
+ for iSes = 1:nbSessions % For each session
+
+ % get all runs for that subject across all sessions
+ [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ % numRuns = group(iGroup).numRuns(iSub);
+ for iRun = 1:nbRuns
+
+ % get the filename for this bold run for this task
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{iSes}, runs{iRun}, opt);
+
+ % check that the file with the right prefix exist
+ files = validationInputFile(subFuncDataDir, fileName, prefix);
+
+ % add the files to list
+ allFilesTemp = cellstr(files);
+ allFiles = [allFiles; allFilesTemp]; %#ok
+ sesCounter = sesCounter + 1;
+
+ end
+ end
+
+ matlabbatch{1}.spm.spatial.smooth.data = allFiles;
+ % Define the amount of smoothing required
+ matlabbatch{1}.spm.spatial.smooth.fwhm = [funcFWHM funcFWHM funcFWHM];
+ matlabbatch{1}.spm.spatial.smooth.dtype = 0;
+ matlabbatch{1}.spm.spatial.smooth.im = 0;
+
+ % Prefix = s+funcFWHM
+ matlabbatch{1}.spm.spatial.smooth.prefix = ...
+ [spm_get_defaults('smooth.prefix'), num2str(funcFWHM)];
+
+end
diff --git a/src/batches/setBatchSubjectLevelContrasts.m b/src/batches/setBatchSubjectLevelContrasts.m
new file mode 100644
index 00000000..34bb15fb
--- /dev/null
+++ b/src/batches/setBatchSubjectLevelContrasts.m
@@ -0,0 +1,25 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSubjectLevelContrasts(opt, subID, funcFWHM)
+
+ fprintf(1, 'BUILDING JOB : FMRI contrasts\n');
+
+ ffxDir = getFFXdir(subID, funcFWHM, opt);
+
+ % Create Contrasts
+ contrasts = specifyContrasts(ffxDir, opt.taskName, opt);
+
+ matlabbatch = [];
+
+ for icon = 1:size(contrasts, 2)
+ matlabbatch{1}.spm.stats.con.consess{icon}.tcon.name = ...
+ contrasts(icon).name;
+ matlabbatch{1}.spm.stats.con.consess{icon}.tcon.convec = ...
+ contrasts(icon).C;
+ matlabbatch{1}.spm.stats.con.consess{icon}.tcon.sessrep = 'none';
+ end
+
+ matlabbatch{1}.spm.stats.con.spmmat = cellstr(fullfile(ffxDir, 'SPM.mat'));
+ matlabbatch{1}.spm.stats.con.delete = 1;
+
+end
diff --git a/src/batches/setBatchSubjectLevelGLMSpec.m b/src/batches/setBatchSubjectLevelGLMSpec.m
new file mode 100644
index 00000000..c07948da
--- /dev/null
+++ b/src/batches/setBatchSubjectLevelGLMSpec.m
@@ -0,0 +1,125 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function matlabbatch = setBatchSubjectLevelGLMSpec(varargin)
+
+ [BIDS, opt, subID, funcFWHM] = deal(varargin{:});
+
+ fprintf(1, 'BUILDING JOB : FMRI design\n');
+
+ % Check the slice timing information is not in the metadata and not added
+ % manually in the opt variable.
+ % Necessary to make sure that the reference slice used for slice time
+ % correction is the one we center our model on
+ sliceOrder = getSliceOrder(opt, 0);
+
+ if isempty(sliceOrder)
+ % no slice order defined here so we fall back on using the number of
+ % slice in the first bold imageBIDS, opt, subID, funcFWHM, iSes, iRun
+ % to set the number of time bins we will use to upsample our model
+ % during regression creation
+ fileName = spm_BIDS(BIDS, 'data', ...
+ 'sub', subID, ...
+ 'type', 'bold');
+ fileName = strrep(fileName{1}, '.gz', '');
+ hdr = spm_vol(fileName);
+ % we are assuming axial acquisition here
+ sliceOrder = 1:hdr(1).dim(3);
+ end
+
+ %%
+ matlabbatch = [];
+
+ matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'secs';
+
+ % get TR from metadata
+ TR = opt.metadata.RepetitionTime;
+ matlabbatch{1}.spm.stats.fmri_spec.timing.RT = TR;
+
+ % number of times bins
+ nbTimeBins = numel(unique(sliceOrder));
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = nbTimeBins;
+
+ refBin = floor(nbTimeBins / 2);
+ matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = refBin;
+
+ % Create ffxDir if it doesnt exist
+ % If it exists, issue a warning that it has been overwritten
+ ffxDir = getFFXdir(subID, funcFWHM, opt);
+ if exist(ffxDir, 'dir') %
+ warning('overwriting directory: %s \n', ffxDir);
+ rmdir(ffxDir, 's');
+ mkdir(ffxDir);
+ end
+ matlabbatch{1}.spm.stats.fmri_spec.dir = {ffxDir};
+
+ matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
+
+ matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
+
+ matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
+
+ matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
+
+ matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
+
+ % The following lines are commented out because those parameters
+ % can be set in the spm_my_defaults.m
+ % matlabbatch{1}.spm.stats.fmri_spec.cvi = 'AR(1)';
+
+ % identify sessions for this subject
+ [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
+
+ sesCounter = 1;
+ for iSes = 1:nbSessions
+
+ % get all runs for that subject across all sessions
+ [runs, nbRuns] = ...
+ getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ for iRun = 1:nbRuns
+
+ % get functional files
+ [fullpathBoldFileName, prefix] = ...
+ getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
+
+ disp(fullpathBoldFileName);
+
+ matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).scans = ...
+ {fullpathBoldFileName};
+
+ % get stimuli onset time file
+ tsvFile = getInfo(BIDS, subID, opt, 'filename', ...
+ sessions{iSes}, ...
+ runs{iRun}, ...
+ 'events');
+ fullpathOnsetFileName = createAndReturnOnsetFile(opt, ...
+ subID, ...
+ tsvFile, ...
+ funcFWHM);
+
+ matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).multi = ...
+ cellstr(fullpathOnsetFileName);
+
+ % get realignment parameters
+ realignParamFile = getRealignParamFile(fullpathBoldFileName, prefix);
+ matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).multi_reg = ...
+ cellstr(realignParamFile);
+
+ % multiregressor selection
+ matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).regress = ...
+ struct('name', {}, 'val', {});
+
+ % multicondition selection
+ matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).cond = ...
+ struct('name', {}, 'onset', {}, 'duration', {});
+
+ % The following lines are commented out because those parameters
+ % can be set in the spm_my_defaults.m
+ % matlabbatch{1}.spm.stats.fmri_spec.sess(ses_counter).hpf = 128;
+
+ sesCounter = sesCounter + 1;
+
+ end
+ end
+
+end
diff --git a/src/concatBetaImgTmaps.m b/src/concatBetaImgTmaps.m
deleted file mode 100644
index 83355dec..00000000
--- a/src/concatBetaImgTmaps.m
+++ /dev/null
@@ -1,130 +0,0 @@
-function concatBetaImgTmaps(funcFWHM, opt, deleteIndBeta, deleteIndTmaps)
- % concatBetaImgTmaps(funcFWHM, opt, deleteIndBeta, deleteIndTmaps)
- %
- % Make 4D images of beta and t-maps for the MVPA
- %
- % Inputs
- % funcFWHM - smoothing (FWHM) applied to the the normalized EPI
- % 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
- %
- % deleteIndBeta, deleteIndTmaps: boolean to decide to delete
- % original t-maps, beta-maps or ResMaps (default = true)
-
- % if input has no opt, load the opt.mat file
- if nargin < 2
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- % delete individual Beta and tmaps
- if nargin < 3
- deleteIndBeta = 1;
- deleteIndTmaps = 1;
- end
-
- % assign isMVPA to true
- isMVPA = 1;
-
- % load the subjects/Groups information and the task name
- [group, opt, ~] = getData(opt);
-
- %% Loop through the groups, subjects
- for iGroup = 1:length(group)
-
- for iSub = 1:group(iGroup).numSub
-
- subID = group(iGroup).subNumber{iSub};
-
- fprintf(1, 'PREPARING: 4D maps: %s \n', subID);
-
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
-
- load(fullfile(ffxDir, 'SPM.mat'));
-
- contrasts = specifyContrasts(ffxDir, opt.taskName, opt, isMVPA);
-
- beta_maps = cell(length(contrasts), 1);
- t_maps = cell(length(contrasts), 1);
-
- % path to beta and t-map files.
- for iContrast = 1:length(beta_maps)
- % Note that the betas are created from the idx (Beta_idx(iBeta))
- fileName = sprintf('beta_%04d.nii', find(contrasts(iContrast).C));
- fileName = inputFileValidation(ffxDir, '', fileName);
- beta_maps{iContrast, 1} = [fileName{1}, ',1'];
-
- % while the contrastes (t-maps) are not from the index. They were created
- fileName = sprintf('spmT_%04d.nii', iContrast);
- fileName = inputFileValidation(ffxDir, '', fileName);
- t_maps{iContrast, 1} = [fileName{1}, ',1'];
- end
-
- % clear previous matlabbatch and files
- matlabbatch = [];
-
- % 4D beta maps
- matlabbatch{1}.spm.util.cat.vols = beta_maps;
- matlabbatch{1}.spm.util.cat.name = ['4D_beta_', num2str(funcFWHM), '.nii'];
- matlabbatch{1}.spm.util.cat.dtype = 4;
-
- % 4D t-maps
- matlabbatch{2}.spm.util.cat.vols = t_maps;
- matlabbatch{2}.spm.util.cat.name = ['4D_t_maps_', num2str(funcFWHM), '.nii'];
- matlabbatch{2}.spm.util.cat.dtype = 4;
-
- saveMatlabBatch(matlabbatch, 'concatBetaImgTmaps', opt, subID);
-
- spm_jobman('run', matlabbatch);
-
- removeBetaImgTmaps(beta_maps, t_maps, deleteIndBeta, deleteIndTmaps);
-
- end
- end
-
-end
-
-function removeBetaImgTmaps(beta_maps, t_maps, deleteIndBeta, deleteIndTmaps)
-
- % delete maps
- if deleteIndBeta
-
- % delete all individual beta maps
- fprintf('Deleting individual beta-maps ... ');
- for iBeta = 1:length(beta_maps)
- delete(beta_maps{iBeta}(1:end - 2));
- end
- fprintf('Done. \n\n\n ');
-
- end
-
- if deleteIndTmaps
-
- % delete all individual con maps
- fprintf('Deleting individual con maps ... ');
- for iCon = 1:length(t_maps)
- delete(fullfile(ffxDir, ['con_', sprintf('%04d', iCon), '.nii']));
- end
- fprintf('Done. \n\n\n ');
-
- % delete all individual t-maps
- fprintf('Deleting individual t-maps ... ');
- for iTmap = 1:length(t_maps)
- delete(t_maps{iTmap}(1:end - 2));
- end
- fprintf('Done. \n\n\n ');
- end
-
- % delete mat files
-
- % This is refactorable
- % ex: delete(fullfile(ffxDir, ['4D_*', num2str(funcFWHM), '.mat']));
-
- if exist(fullfile(ffxDir, ['4D_beta_', num2str(funcFWHM), '.mat']), 'file')
- delete(fullfile(ffxDir, ['4D_beta_', num2str(funcFWHM), '.mat']));
- end
- if exist(fullfile(ffxDir, ['4D_t_maps_', num2str(funcFWHM), '.mat']), 'file')
- delete(fullfile(ffxDir, ['4D_t_maps_', num2str(funcFWHM), '.mat']));
- end
-end
diff --git a/src/convertOnsetTsvToMat.m b/src/convertOnsetTsvToMat.m
deleted file mode 100644
index 2fef93d8..00000000
--- a/src/convertOnsetTsvToMat.m
+++ /dev/null
@@ -1,78 +0,0 @@
-function fullpathOnsetFileName = convertOnsetTsvToMat(opt, tsvFile, isMVPA)
- %% Converts a tsv file to an onset file suitable for SPM ffx analysis
- % The scripts extracts the conditions' names, onsets, and durations, and
- % converts them to TRs (time unit) and saves the onset file to be used for
- % SPM
- %%
-
- if ~exist(tsvFile, 'file')
-
- errorStruct.identifier = 'convertOnsetTsvToMat:nonExistentFile';
- errorStruct.message = sprintf('%s\n%s', ...
- 'This onset tsv file deos not exist:', ...
- tsvFile);
- error(errorStruct);
- end
-
- % Read the tsv file
- fprintf('reading the tsv file : %s \n', tsvFile);
- t = spm_load(tsvFile);
-
- if ~isfield(t, 'trial_type')
-
- errorStruct.identifier = 'convertOnsetTsvToMat:noTrialType';
- errorStruct.message = sprintf('%s\n%s', ...
- 'There was no trial_type field in this file:', ...
- tsvFile);
- error(errorStruct);
-
- end
-
- conds = t.trial_type; % assign all the tsv information to a variable called conds.
-
- % identify where the conditions to include that are specificed in the 'un' step of the
- % model file
- if isMVPA
- model = spm_jsonread(opt.model.multivariate.file);
- else
- model = spm_jsonread(opt.model.univariate.file);
- end
-
- for runIdx = 1:numel(model.Steps)
- if strcmp(model.Steps{1}.Level, 'run')
- break
- end
- end
-
- isTrialType = strfind(model.Steps{runIdx}.Model.X, 'trial_type.');
-
- % for each condition
- for iCond = 1:numel(isTrialType)
-
- if isTrialType{iCond}
-
- conditionName = strrep(model.Steps{runIdx}.Model.X{iCond}, ...
- 'trial_type.', ...
- '');
-
- % Get the index of each condition by comparing the unique names and
- % each line in the tsv files
- idx = find(strcmp(conditionName, conds));
-
- % Get the onset and duration of each condition
- names{1, iCond} = conditionName;
- onsets{1, iCond} = t.onset(idx)'; %#ok<*AGROW,*NASGU>
- durations{1, iCond} = t.duration(idx)';
-
- end
- end
-
- % save the onsets as a matfile
- [path, file] = spm_fileparts(tsvFile);
-
- fullpathOnsetFileName = fullfile(path, ['onsets_' file '.mat']);
-
- save(fullpathOnsetFileName, ...
- 'names', 'onsets', 'durations');
-
-end
diff --git a/src/createAndReturnOnsetFile.m b/src/createAndReturnOnsetFile.m
deleted file mode 100644
index b364439c..00000000
--- a/src/createAndReturnOnsetFile.m
+++ /dev/null
@@ -1,28 +0,0 @@
-function onsetFileName = createAndReturnOnsetFile(opt, subID, funcFWHM, boldFileName, isMVPA)
- % onsetFileName = createAndReturnOnsetFile(opt, boldFileName, prefix, isMVPA)
- %
- % gets the tsv onset file based on the bold file name (removes any prefix)
- %
- % convert the tsv files to a mat file to be used by SPM
-
- prefix = getPrefix('FFX', opt, funcFWHM);
- if strcmp(opt.space, 'T1w')
- prefix = getPrefix('FFX_space-T1w', opt, funcFWHM);
- end
-
- [funcDataDir, boldFileName] = spm_fileparts(boldFileName{1});
-
- tsvFile = strrep(boldFileName, '_bold', '_events.tsv');
- tsvFile = strrep(tsvFile, prefix, '');
- tsvFile = fullfile(funcDataDir, tsvFile);
-
- onsetFileName = convertOnsetTsvToMat(opt, tsvFile, isMVPA);
-
- % move file into the FFX directory
- [~, filename, ext] = spm_fileparts(onsetFileName);
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
- copyfile(onsetFileName, ffxDir);
-
- onsetFileName = fullfile(ffxDir, [filename ext]);
-
-end
diff --git a/src/defaults/checkOptions.m b/src/defaults/checkOptions.m
new file mode 100644
index 00000000..b87cd0bf
--- /dev/null
+++ b/src/defaults/checkOptions.m
@@ -0,0 +1,115 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+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;
+
+ % any voxel with p(grayMatter) + p(whiteMatter) + p(CSF) > threshold
+ % will be included in the skull stripping mask
+ fieldsToSet.skullstrip.threshold = 0.75;
+
+ % 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
+
+ % 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 = [];
+
+ % 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/miss_hit.cfg b/src/defaults/miss_hit.cfg
similarity index 53%
rename from src/miss_hit.cfg
rename to src/defaults/miss_hit.cfg
index d3504bda..636a9186 100644
--- a/src/miss_hit.cfg
+++ b/src/defaults/miss_hit.cfg
@@ -1,10 +1,10 @@
# styly guide (https://florianschanda.github.io/miss_hit/style_checker.html)
line_length: 100
-regex_function_name: "[a-z]+(([A-Z0-9]){1}[A-Za-z]+)*"
-suppress_rule: "copyright_notice"
+regex_function_name: "[a-z]+(_*([a-zA-Z0-9]){1}[A-Za-z]+)*" # almost anything goes in the root folder
+copyright_entity: "CPP BIDS SPM-pipeline developpers"
# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
metric "cnest": limit 4
-metric "file_length": limit 500
-metric "cyc": limit 15
+metric "file_length": limit 400
+metric "cyc": limit 12
metric "parameters": limit 6
\ No newline at end of file
diff --git a/src/defaults/spm_my_defaults.m b/src/defaults/spm_my_defaults.m
new file mode 100644
index 00000000..f2cf37d7
--- /dev/null
+++ b/src/defaults/spm_my_defaults.m
@@ -0,0 +1,233 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function spm_my_defaults
+ % This is where we set the defautls we want to use.
+ % These will overide the spm defaults.
+ % When "not enough" information is specified in the batch files, SPM falls
+ % back on the defaults to fill in the blanks. This allows to make the
+ % script simpler.
+
+ global defaults
+
+ % User Interface defaults
+ % ==========================================================================
+ defaults.ui.print = 'png';
+
+ % File format specific
+ % ==========================================================================
+ defaults.mat.format = '-v7';
+
+ % Realignment defaults
+ % ==========================================================================
+ defaults.realign.estimate.quality = 1;
+ defaults.realign.estimate.interp = 2;
+ defaults.realign.estimate.wrap = [0 0 0];
+ defaults.realign.estimate.sep = 2;
+ defaults.realign.estimate.fwhm = 5;
+ defaults.realign.estimate.rtm = 0;
+ defaults.realign.write.mask = 1;
+ defaults.realign.write.interp = 3;
+ defaults.realign.write.wrap = [0 0 0];
+ defaults.realign.write.which = [0 1];
+
+ % Coregistration defaults
+ % ==========================================================================
+ defaults.coreg.estimate.cost_fun = 'nmi';
+ defaults.coreg.estimate.sep = [4 2];
+ defaults.coreg.estimate.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];
+ defaults.coreg.estimate.fwhm = [7 7];
+
+ % Spatial Normalisation defaults
+ % ==========================================================================
+ defaults.normalise.write.preserve = 0;
+ defaults.normalise.write.bb = [[-78 -112 -70]; [78 76 85]];
+ defaults.normalise.write.interp = 4;
+
+ % fMRI design defaults
+ % ==========================================================================
+ defaults.stats.fmri.t = 16;
+ defaults.stats.fmri.t0 = 8;
+ defaults.stats.fmri.hpf = 128;
+ defaults.stats.fmri.cvi = 'FAST';
+
+ % Filename prefix defaults
+ % ==========================================================================
+ defaults.slicetiming.prefix = 'a';
+ defaults.realign.write.prefix = 'r';
+ defaults.coreg.write.prefix = 'r';
+ defaults.unwarp.write.prefix = 'u';
+ defaults.normalise.write.prefix = 'w';
+ defaults.deformations.modulate.prefix = 'm';
+ defaults.smooth.prefix = 's';
+ defaults.imcalc.prefix = 'i';
+
+ % Set the defaults which are used by SPM
+ % __________________________________________________________________________
+ %
+ % If you want to customise some defaults for your installation, do not
+ % modify this file directly, but create a file named spm_my_defaults.m
+ % instead, accessible from MATLAB search path; e.g., it can be saved in
+ % MATLAB Startup Folder: userhome/Documents/MATLAB.
+ %
+ % Example: create the following file to change the image file extension:
+ % ----------- file /home/karl/Documents/MATLAB/spm_my_defaults.m -----------
+ % global defaults
+ % defaults.images.format = 'img';
+ % --------------------------------------------------------------------------
+ %
+ % spm_defaults should not be called directly in any script or function
+ % (apart from SPM internals).
+ % To load the defaults, use spm('Defaults',modality).
+ % To get/set the defaults, use spm_get_defaults.
+
+ % % User Interface defaults
+ % %==========================================================================
+ % defaults.ui.monitor = NaN;
+ % defaults.ui.colour = [0.58 0.77 0.57];
+ % defaults.ui.fs = 14; % unused
+ % defaults.ui.print = 'ps';
+ % defaults.renderer = 'opengl';
+ %
+ % % File format specific
+ % %==========================================================================
+ % % Note that defaults.analyze.flip is no longer used. Specifying the
+ % % left/right handedness of the voxel indices is now done entirely by
+ % % spm_flip_analyze_images.m
+ % defaults.images.format = 'nii'; % options: 'img', 'nii'
+ % defaults.mat.format = '-v6'; % options: '-mat', '-v6', '-v7.0', '-v7.3'
+ %
+ % % Toolboxes defaults
+ % %==========================================================================
+ % defaults.tbx.dir = { fullfile(spm('Dir'),'toolbox') };
+ %
+ % % DICOM Import defaults
+ % %==========================================================================
+ % defaults.dicom.root = 'flat'; % Folder hierarchy
+ %
+ % % fMRI design defaults
+ % %==========================================================================
+ % defaults.stats.fmri.t = 16;
+ % defaults.stats.fmri.t0 = 8;
+ % defaults.stats.fmri.hpf = 128;
+ % defaults.stats.fmri.cvi = 'AR(1)';
+ % defaults.stats.fmri.hrf = [6 16 1 1 6 0 32];
+ %
+ % % Mask defaults
+ % %==========================================================================
+ % defaults.mask.thresh = 0.8;
+ %
+ % % Stats defaults
+ % %==========================================================================
+ % defaults.stats.maxmem = 2^29;
+ % defaults.stats.maxres = 64;
+ % defaults.stats.resmem = false;
+ % defaults.stats.fmri.ufp = 0.001; % Upper tail F-probability
+ % defaults.stats.pet.ufp = 0.05;
+ % defaults.stats.eeg.ufp = 0.05;
+ % defaults.stats.topoFDR = 1;
+ % defaults.stats.rft.nonstat = 0;
+ % defaults.stats.results.volume.distmin = 8;
+ % defaults.stats.results.volume.nbmax = 3;
+ % defaults.stats.results.svc.distmin = 4;
+ % defaults.stats.results.svc.nbmax = 16;
+ % defaults.stats.results.mipmat = {fullfile(spm('dir'),'MIP.mat')};
+ %
+ % % Filename prefix defaults
+ % %==========================================================================
+ % defaults.slicetiming.prefix = 'a';
+ % defaults.realign.write.prefix = 'r';
+ % defaults.coreg.write.prefix = 'r';
+ % defaults.unwarp.write.prefix = 'u';
+ % defaults.normalise.write.prefix = 'w';
+ % defaults.deformations.modulate.prefix = 'm';
+ % defaults.smooth.prefix = 's';
+ % defaults.imcalc.prefix = 'i';
+ %
+ % % Realignment defaults
+ % %==========================================================================
+ % defaults.realign.estimate.quality = 0.9;
+ % defaults.realign.estimate.interp = 2;
+ % defaults.realign.estimate.wrap = [0 0 0];
+ % defaults.realign.estimate.sep = 4;
+ % defaults.realign.estimate.fwhm = 5;
+ % defaults.realign.estimate.rtm = 1;
+ % defaults.realign.write.mask = 1;
+ % defaults.realign.write.interp = 4;
+ % defaults.realign.write.wrap = [0 0 0];
+ % defaults.realign.write.which = [2 1];
+ %
+ % % Unwarp defaults
+ % %==========================================================================
+ % defaults.unwarp.estimate.rtm = 0;
+ % defaults.unwarp.estimate.fwhm = 4;
+ % defaults.unwarp.estimate.basfcn = [12 12];
+ % defaults.unwarp.estimate.regorder = 1;
+ % defaults.unwarp.estimate.regwgt = 1e5;
+ % defaults.unwarp.estimate.foe = [4 5];
+ % defaults.unwarp.estimate.soe = [];
+ % defaults.unwarp.estimate.rem = 1;
+ % defaults.unwarp.estimate.jm = 0;
+ % defaults.unwarp.estimate.noi = 5;
+ % defaults.unwarp.estimate.expround = 'Average';
+ % %
+ % % Unwarp uses defaults.realign.write defaults for writing.
+ % %
+ %
+ % % Coregistration defaults
+ % %==========================================================================
+ % defaults.coreg.estimate.cost_fun = 'nmi';
+ % defaults.coreg.estimate.sep = [4 2];
+ % defaults.coreg.estimate.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];
+ % defaults.coreg.estimate.fwhm = [7 7];
+ % defaults.coreg.write.interp = 4;
+ % defaults.coreg.write.wrap = [0 0 0];
+ % defaults.coreg.write.mask = 0;
+ %
+ % % Spatial Normalisation defaults
+ % %==========================================================================
+ % defaults.normalise.write.preserve = 0;
+ % defaults.normalise.write.bb = [[-78 -112 -70];[78 76 85]];
+ % defaults.normalise.write.vox = [2 2 2];
+ % defaults.normalise.write.interp = 4;
+ %
+ % % Old Spatial Normalisation defaults
+ % %==========================================================================
+ % defaults.old.normalise.estimate.smosrc = 8;
+ % defaults.old.normalise.estimate.smoref = 0;
+ % defaults.old.normalise.estimate.regtype = 'mni';
+ % defaults.old.normalise.estimate.weight = '';
+ % defaults.old.normalise.estimate.cutoff = 25;
+ % defaults.old.normalise.estimate.nits = 16;
+ % defaults.old.normalise.estimate.reg = 1;
+ % defaults.old.normalise.write.preserve = 0;
+ % defaults.old.normalise.write.bb = [[-78 -112 -70];[78 76 85]];
+ % defaults.old.normalise.write.vox = [2 2 2];
+ % defaults.old.normalise.write.interp = 1;
+ % defaults.old.normalise.write.wrap = [0 0 0];
+ % defaults.old.normalise.write.prefix = 'w';
+ %
+ % % VBM Preprocessing defaults
+ % %==========================================================================
+ % defaults.old.preproc.tpm = cellstr(char(...
+ % fullfile(spm('Dir'),'toolbox','OldSeg','grey.nii'),...
+ % fullfile(spm('Dir'),'toolbox','OldSeg','white.nii'),...
+ % fullfile(spm('Dir'),'toolbox','OldSeg','csf.nii'))); % Prior probability maps
+ % defaults.old.preproc.ngaus = [2 2 2 4]'; % Gaussians per class
+ % defaults.old.preproc.warpreg = 1; % Warping Regularisation
+ % defaults.old.preproc.warpco = 25; % Warp Frequency Cutoff
+ % defaults.old.preproc.biasreg = 0.0001; % Bias regularisation
+ % defaults.old.preproc.biasfwhm = 60; % Bias FWHM
+ % defaults.old.preproc.regtype = 'mni'; % Affine Regularisation
+ % defaults.old.preproc.fudge = 5; % Fudge factor, can not be set in batch GUI
+ % defaults.old.preproc.samp = 3; % Sampling distance
+ % defaults.old.preproc.output.GM = [0 0 1];
+ % defaults.old.preproc.output.WM = [0 0 1];
+ % defaults.old.preproc.output.CSF = [0 0 0];
+ % defaults.old.preproc.output.biascor = 1;
+ % defaults.old.preproc.output.cleanup = 0;
+ %
+ % % Smooth defaults
+ % %==========================================================================
+ % defaults.smooth.fwhm = [8 8 8];
diff --git a/src/deleteResidualImages.m b/src/deleteResidualImages.m
deleted file mode 100644
index 2f0bc100..00000000
--- a/src/deleteResidualImages.m
+++ /dev/null
@@ -1,3 +0,0 @@
-function deleteResidualImages(ffxDir)
- delete(fullfile(ffxDir, 'Res_*.nii'));
-end
diff --git a/src/fieldmaps/getBlipDirection.m b/src/fieldmaps/getBlipDirection.m
new file mode 100644
index 00000000..33bbb2db
--- /dev/null
+++ b/src/fieldmaps/getBlipDirection.m
@@ -0,0 +1,37 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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;
+
+ 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/fieldmaps/getMetadataFromIntendedForFunc.m b/src/fieldmaps/getMetadataFromIntendedForFunc.m
new file mode 100644
index 00000000..5f5b1218
--- /dev/null
+++ b/src/fieldmaps/getMetadataFromIntendedForFunc.m
@@ -0,0 +1,63 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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/fieldmaps/getTotalReadoutTime.m b/src/fieldmaps/getTotalReadoutTime.m
new file mode 100644
index 00000000..f9f2122a
--- /dev/null
+++ b/src/fieldmaps/getTotalReadoutTime.m
@@ -0,0 +1,66 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function totalReadoutTime = getTotalReadoutTime(metadata)
+ %
+ % 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 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.');
+ %
+ % % 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;
+
+ %% 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/fieldmaps/getVdmFile.m b/src/fieldmaps/getVdmFile.m
new file mode 100644
index 00000000..b83dfade
--- /dev/null
+++ b/src/fieldmaps/getVdmFile.m
@@ -0,0 +1,67 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function vdmFile = getVdmFile(BIDS, opt, boldFilename)
+ %
+ % returns the voxel displacement map associated with a given bold file
+ %
+ % USAGE::
+ %
+ % vdmFile = getVdmFile(BIDS, opt, boldFilename)
+ %
+ % :param BIDS:
+ % :type BIDS: structure
+ % :param opt: options
+ % :type opt: structure
+ % :param boldFilename:
+ % :type opt: string
+ %
+ % :returns: - :vdmFile: (string)
+ %
+
+ 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))
+ % 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', ...
+ '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
+
+ if isempty(vdmFile)
+ warning('No voxel displacement map associated with: \n %s', boldFilename);
+ end
+
+end
diff --git a/src/getAnatFilename.m b/src/getAnatFilename.m
new file mode 100644
index 00000000..e1361e47
--- /dev/null
+++ b/src/getAnatFilename.m
@@ -0,0 +1,30 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt)
+ % [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt)
+ %
+ % Get the filename and the directory of an anat file for a given session /
+ % run.
+ % Unzips the file if necessary.
+
+ anatType = opt.anatReference.type;
+
+ % TODO allow for the session to be referenced by a string e.g ses-retest
+ anatSession = opt.anatReference.session;
+
+ % get all anat images for that subject fo that type
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
+ anat = spm_BIDS(BIDS, 'data', ...
+ 'sub', subID, ...
+ 'ses', sessions{anatSession}, ...
+ 'type', anatType);
+
+ % TODO
+ % We assume that the first anat of that type is the correct one
+ % could be an issue for dataset with more than one anatomical of the same type
+ anat = anat{1};
+ anatImage = unzipImgAndReturnsFullpathName(anat);
+
+ [anatDataDir, anatImage, ext] = spm_fileparts(anatImage);
+ anatImage = [anatImage ext];
+end
diff --git a/src/getBoldFilename.m b/src/getBoldFilename.m
index 3e26bc6a..bd453c1e 100644
--- a/src/getBoldFilename.m
+++ b/src/getBoldFilename.m
@@ -1,19 +1,22 @@
-function [fileName, subFuncDataDir] = getBoldFilename(varargin)
- % [fileName, subFuncDataDir] = getBoldFilename(BIDS, opt, subID, sessionID, runID)
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
- [BIDS, subID, sessionID, runID, opt] = deal(varargin{:});
+function [boldFileName, subFuncDataDir] = getBoldFilename(varargin)
+ % [fileName, subFuncDataDir] = getBoldFilename(BIDS, opt, subID, sessionID, runID)
+ %
+ % Get the filename and the directory of a bold file for a given session /
+ % run.
+ % Unzips the file if necessary.
- % get the filename for this bold run for this task
- fileName = getInfo(BIDS, subID, opt, 'Filename', sessionID, runID, 'bold');
+ [BIDS, subID, sessionID, runID, opt] = deal(varargin{:});
- % get fullpath of the file
- fileName = fileName{1};
- [subFuncDataDir, file, ext] = spm_fileparts(fileName);
- % get filename of the orginal file (drop the gunzip extension)
- if strcmp(ext, '.gz')
- fileName = file;
- elseif strcmp(ext, '.nii')
- fileName = [file ext];
- end
+ % get the filename for this bold run for this task
+ boldFileName = getInfo(BIDS, subID, opt, 'Filename', sessionID, runID, 'bold');
+
+ % get fullpath of the file
+ % ASSUMPTION: the first file is the right one.
+ boldFileName = unzipImgAndReturnsFullpathName(boldFileName);
+
+ [subFuncDataDir, boldFileName, ext] = spm_fileparts(boldFileName);
+ boldFileName = [boldFileName ext];
end
diff --git a/src/getBoldFilenameForFFX.m b/src/getBoldFilenameForFFX.m
deleted file mode 100644
index 802be92b..00000000
--- a/src/getBoldFilenameForFFX.m
+++ /dev/null
@@ -1,29 +0,0 @@
-function [boldFileName, prefix] = getBoldFilenameForFFX(varargin)
- % [boldFileName, prefix] = getFunctionalFiles(BIDS, opt, subID, funcFWHM, iSes, iRun)
- %
- % get the filename for this bold run for this task for the FFX setup
- % and check that the file with the right prefix exist
-
- [BIDS, opt, subID, funcFWHM, iSes, iRun] = deal(varargin{:});
-
- sessions = getInfo(BIDS, subID, opt, 'Sessions');
-
- runs = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
-
- prefix = getPrefix('FFX', opt, funcFWHM);
- if strcmp(opt.space, 'T1w')
- prefix = getPrefix('FFX_space-T1w', opt, funcFWHM);
- end
-
- [fileName, subFuncDataDir] = getBoldFilename( ...
- BIDS, ...
- subID, sessions{iSes}, runs{iRun}, opt);
-
- boldFileName = inputFileValidation( ...
- subFuncDataDir, ...
- prefix, ...
- fileName);
-
- disp(boldFileName);
-
-end
diff --git a/src/getData.m b/src/getData.m
index dd51530b..956dea12 100644
--- a/src/getData.m
+++ b/src/getData.m
@@ -1,165 +1,135 @@
-function [group, opt, BIDS] = getData(opt, BIDSdir, type)
- % getData checks that all the options specified by the user in getOptions
- % and fills the blank for any that might have been missed out.
- % It then reads the specified BIDS data set and gets the groups and
- % subjects to analyze. This can be specified in the opt structure in
- % different ways:
- % Set the group of subjects to analyze.
- % opt.groups = {'control', 'blind'};
- %
- % If there are no groups (i.e subjects names are of the form `sub-01` for
- % example) or if you want to run all subjects of all groups then use:
- % opt.groups = {''};
- % opt.subjects = {[]};
- %
- % If you have 2 groups (`cont` and `cat` for example) the following will
- % run cont01, cont02, cat03, cat04.
- % opt.groups = {'cont', 'cat'};
- % opt.subjects = {[1 2], [3 4]};
- %
- % If you have more than 2 groups but want to only run the subjects of 2
- % groups then you can use.
- % opt.groups = {'cont', 'cat'};
- % opt.subjects = {[], []};
- %
- % You can also directly specify the subject label for the participants you want to run
- % opt.groups = {''};
- % opt.subjects = {'01', 'cont01', 'cat02', 'ctrl02', 'blind01'};
- %
- % You can also specify:
- % - BIDSdir: the directory where the data is ; default is :
- % fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL')
- % - type: the data type you want to get the metadata of ;
- % supported: bold (default) and T1w
- %
- % IMPORTANT NOTE: if you specify the type variable for T1w then you must
- % make sure that the T1w.json is also present in the anat folder because
- % of the way the spm_BIDS function works at the moment
-
- opt = checkOptions(opt);
-
- if nargin < 2 || (exist('BIDSdir', 'var') && isempty(BIDSdir))
- % The directory where the derivatives are located
- BIDSdir = fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL');
- end
- derivativesDir = BIDSdir;
-
- if nargin < 3 || (exist('type', 'var') && isempty(type))
- type = 'bold';
- end
-
- fprintf(1, 'FOR TASK: %s\n', opt.taskName);
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
- % we let SPM figure out what is in this BIDS data set
- BIDS = spm_BIDS(derivativesDir);
-
- % make sure that the required tasks exist in the data set
- if ~ismember(opt.taskName, spm_BIDS(BIDS, 'tasks'))
- fprintf('List of tasks present in this dataset:\n');
- spm_BIDS(BIDS, 'tasks');
- error('The task %s that you have asked for does not exist in this data set.');
+function [group, opt, BIDS] = getData(opt, BIDSdir, type)
+ % [group, opt, BIDS] = getData(opt, BIDSdir, type)
+ %
+ % getData checks that all the options specified by the user in getOptions
+ % and fills the blank for any that might have been missed out.
+ % It then reads the specified BIDS data set and gets the groups and
+ % subjects to analyze. This can be specified in the opt structure in
+ % different ways:
+ % Set the group of subjects to analyze.
+ % opt.groups = {'control', 'blind'};
+ %
+ % If there are no groups (i.e subjects names are of the form `sub-01` for
+ % example) or if you want to run all subjects of all groups then use:
+ % opt.groups = {''};
+ % opt.subjects = {[]};
+ %
+ % If you have 2 groups (`cont` and `cat` for example) the following will
+ % run cont01, cont02, cat03, cat04.
+ % opt.groups = {'cont', 'cat'};
+ % opt.subjects = {[1 2], [3 4]};
+ %
+ % If you have more than 2 groups but want to only run the subjects of 2
+ % groups then you can use.
+ % opt.groups = {'cont', 'cat'};
+ % opt.subjects = {[], []};
+ %
+ % You can also directly specify the subject label for the participants you want to run
+ % opt.groups = {''};
+ % opt.subjects = {'01', 'cont01', 'cat02', 'ctrl02', 'blind01'};
+ %
+ % You can also specify:
+ % - BIDSdir: the directory where the data is ; default is :
+ % fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL')
+ % - type: the data type you want to get the metadata of ;
+ % supported: bold (default) and T1w
+ %
+ % IMPORTANT NOTE: if you specify the type variable for T1w then you must
+ % make sure that the T1w.json is also present in the anat folder because
+ % of the way the spm_BIDS function works at the moment
+
+ if nargin < 2 || (exist('BIDSdir', 'var') && isempty(BIDSdir))
+ % The directory where the derivatives are located
+ opt = setDerivativesDir(opt);
+ BIDSdir = opt.derivativesDir;
+ end
+ derivativesDir = BIDSdir;
+
+ if nargin < 3 || (exist('type', 'var') && isempty(type))
+ type = 'bold';
+ end
+
+ fprintf(1, 'FOR TASK: %s\n', opt.taskName);
+
+ % we let SPM figure out what is in this BIDS data set
+ BIDS = spm_BIDS(derivativesDir);
+
+ % make sure that the required tasks exist in the data set
+ if ~ismember(opt.taskName, spm_BIDS(BIDS, 'tasks'))
+ fprintf('List of tasks present in this dataset:\n');
+ spm_BIDS(BIDS, 'tasks');
+
+ errorStruct.identifier = 'getData:noMatchingTask';
+ errorStruct.message = sprintf( ...
+ ['The task %s that you have asked for ', ...
+ 'does not exist in this data set.'], opt.taskName);
+ error(errorStruct);
+ end
+
+ % get IDs of all subjects
+ subjects = spm_BIDS(BIDS, 'subjects');
+
+ % get metadata for bold runs for that task
+ % we take those from the first run of the first subject assuming it can
+ % apply to all others.
+ opt = getMetaData(BIDS, opt, subjects, type);
+
+ %% Add the different groups in the experiment
+ for iGroup = 1:numel(opt.groups) % for each group
+
+ clear idx;
+
+ % Name of the group
+ group(iGroup).name = opt.groups{iGroup}; %#ok<*AGROW>
+
+ group = getSpecificSubjects(opt, group, iGroup, subjects);
+
+ % check that all the subjects asked for exist
+ if ~all(ismember(group(iGroup).subNumber, subjects))
+ fprintf('subjects specified\n');
+ disp(group(iGroup).subNumber);
+ fprintf('subjects present\n');
+ disp(subjects);
+
+ errorStruct.identifier = 'getData:noMatchingSubject';
+ msg = ['Some of the subjects specified do not exist in this data set.' ...
+ 'This can be due to wrong zero padding: see opt.zeropad in getOptions'];
+ errorStruct.message = msg;
+ error(errorStruct);
end
- % get IDs of all subjects
- subjects = spm_BIDS(BIDS, 'subjects');
-
- % get metadata for bold runs for that task
- % we take those from the first run of the first subject assuming it can
- % apply to all others.
- opt = getMetaData(BIDS, opt, subjects, type);
-
- %% Add the different groups in the experiment
- for iGroup = 1:numel(opt.groups) % for each group
+ % Number of subjects in the group
+ group(iGroup).numSub = length(group(iGroup).subNumber);
- clear idx;
-
- % Name of the group
- group(iGroup).name = opt.groups{iGroup}; %#ok<*AGROW>
-
- group = getSpecificSubjects(opt, group, iGroup, subjects);
-
- % check that all the subjects asked for exist
- if ~all(ismember(group(iGroup).subNumber, subjects))
- fprintf('subjects specified\n');
- disp(group(iGroup).subNumber);
- fprintf('subjects present\n');
- disp(subjects);
- error([ ...
- 'Some of the subjects specified do not exist in this data set.' ...
- 'This can be due to wrong zero padding: see opt.zeropad in getOptions']);
- end
-
- % Number of subjects in the group
- group(iGroup).numSub = length(group(iGroup).subNumber);
-
- fprintf(1, 'WILL WORK ON SUBJECTS\n');
- disp(group(iGroup).subNumber);
- end
+ fprintf(1, 'WILL WORK ON SUBJECTS\n');
+ disp(group(iGroup).subNumber);
+ end
end
function opt = getMetaData(BIDS, opt, subjects, type)
- % THIS NEEDS FIXING AS WE MIGHT WANT THE METADATA OF THE SUBJECTS SELECTED
- % RATHER THAN THE FIRST SUBJECT OF THE DATASET
-
- switch type
- case 'bold'
- metadata = spm_BIDS(BIDS, 'metadata', ...
- 'task', opt.taskName, ...
- 'sub', subjects{1}, ...
- 'type', type);
- case 'T1w'
- metadata = spm_BIDS(BIDS, 'metadata', ...
- 'sub', subjects{1}, ...
- 'type', [type]);
- end
-
- if iscell(metadata)
- opt.metadata = metadata{1};
- else
- opt.metadata = metadata;
- end
-
-end
-
-function group = getSpecificSubjects(opt, group, iGroup, subjects)
-
- % if no group or subject was specified we take all of them
- if numel(opt.groups) == 1 && ...
- strcmp(group(iGroup).name, '') && ...
- isempty(opt.subjects{iGroup})
-
- group(iGroup).subNumber = subjects;
-
- % if subject ID were directly specified by users we take those
- elseif strcmp(group(iGroup).name, '') && iscellstr(opt.subjects)
-
- group(iGroup).subNumber = opt.subjects;
-
- % if group was specified we figure out which subjects to take
- elseif ~isempty(opt.subjects{iGroup})
-
- idx = opt.subjects{iGroup};
-
- % else we take all subjects of that group
- elseif isempty(opt.subjects{iGroup})
-
- % count how many subjects in that group
- idx = sum(~cellfun(@isempty, strfind(subjects, group(iGroup).name)));
- idx = 1:idx;
-
- else
-
- error('Not sure what to do.');
-
- end
-
- % if only indices were specified we get the subject from that group with that
- if exist('idx', 'var')
- pattern = [group(iGroup).name '%0' num2str(opt.zeropad) '.0f_'];
- temp = strsplit(sprintf(pattern, idx), '_');
- group(iGroup).subNumber = temp(1:end - 1);
- end
+ % THIS NEEDS FIXING AS WE MIGHT WANT THE METADATA OF THE SUBJECTS SELECTED
+ % RATHER THAN THE FIRST SUBJECT OF THE DATASET
+
+ switch type
+ case 'bold'
+ metadata = spm_BIDS(BIDS, 'metadata', ...
+ 'task', opt.taskName, ...
+ 'sub', subjects{1}, ...
+ 'type', type);
+ case 'T1w'
+ metadata = spm_BIDS(BIDS, 'metadata', ...
+ 'sub', subjects{1}, ...
+ 'type', type);
+ end
+
+ if iscell(metadata)
+ opt.metadata = metadata{1};
+ else
+ opt.metadata = metadata;
+ end
end
diff --git a/src/getFFXdir.m b/src/getFFXdir.m
deleted file mode 100644
index bf4fde37..00000000
--- a/src/getFFXdir.m
+++ /dev/null
@@ -1,19 +0,0 @@
-function ffxDir = getFFXdir(subID, funcFWFM, opt, isMVPA)
- % ffxDir = getFFXdir(subID, funcFWFM, opt, isMVPA)
- %
- % sets the name the FFX directory and creates it if it does not exist
- %
- %
-
- mvpaSuffix = setMvpaSuffix(isMVPA);
-
- ffxDir = fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL', ...
- ['sub-', subID], ...
- 'stats', ...
- ['ffx_task-', opt.taskName], ...
- ['ffx_FWHM-', num2str(funcFWFM), mvpaSuffix]);
-
- if ~exist(ffxDir, 'dir')
- mkdir(ffxDir);
- end
-end
diff --git a/src/getFuncVoxelDims.m b/src/getFuncVoxelDims.m
new file mode 100644
index 00000000..8ac87338
--- /dev/null
+++ b/src/getFuncVoxelDims.m
@@ -0,0 +1,26 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName)
+ % [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName)
+ %
+ %
+
+ % get native resolution to reuse it at normalisation;
+ if ~isempty(opt.funcVoxelDims) % If voxel dimensions is defined in the opt
+ voxDim = opt.funcVoxelDims; % Get the dimension values
+ return
+ else
+ % SPM Doesnt deal with nii.gz and all our nii should be unzipped
+ % at this stage
+ hdr = spm_vol(fullfile(subFuncDataDir, [prefix, fileName]));
+ voxDim = diag(hdr(1).mat);
+ % Voxel dimensions are not pure integers before reslicing, therefore
+ % round the dimensions of the functional files to the 1st decimal point
+ voxDim = abs(voxDim(1:3)');
+ voxDim = round(voxDim * 10) / 10;
+ % Add it to opt.funcVoxelDims to have the same value for
+ % all subjects and sessions
+ opt.funcVoxelDims = voxDim;
+ end
+
+end
diff --git a/src/getInfo.m b/src/getInfo.m
index eafc69f8..8b137b9a 100644
--- a/src/getInfo.m
+++ b/src/getInfo.m
@@ -1,76 +1,85 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
function varargout = getInfo(BIDS, subID, opt, info, varargin)
- % for a given BIDS data set, subject identity, and info type,
- % if info = Sessions, this returns name of the sessions and their number
- % if info = Runs, this returns name of the runs and their number for an specified session.
- % if info = Filename, this returns the name of the file for an specified
- % session and run.
- %
- % INPUTS
- % BIDS - variable returned by spm_BIDS when exploring a BIDS data set
- % subID - ID of the subject ; in BIDS lingo that means that for a file name
- % info - either 1)Runs, 2)Sessions or 3) Filename.
- % sub-02_task-foo_bold.nii the subID will be the string '02'
- % session - ID of the session of interes ; in BIDS lingo that means that for a file name
- % sub-02_ses-pretest_task-foo_bold.nii the sesssion will be the string 'pretest'
- % run - ID of the run of interes
- % type - string ; modality type to look for. For example: 'bold', 'events',
- % 'stim', 'physio'...
- % opt - options structure defined by the getOption function. Mostly used to find the
- % task name.
-
- info = lower(info);
-
- varargout = {}; %#ok<*NASGU>
-
- switch info
-
- case 'sessions'
-
- sessions = spm_BIDS(BIDS, 'sessions', ...
- 'sub', subID, ...
- 'task', opt.taskName);
- nbSessions = size(sessions, 2);
- if nbSessions == 0
- nbSessions = 1;
- sessions = {''};
- end
-
- varargout = {sessions, nbSessions};
-
- case 'runs'
-
- session = varargin{1};
-
- runs = spm_BIDS(BIDS, 'runs', ...
- 'sub', subID, ...
- 'task', opt.taskName, ...
- 'ses', session, ...
- 'type', 'bold');
- nbRuns = size(runs, 2); % Get the number of runs
-
- if nbRuns == 0
- nbRuns = 1;
- runs = {''};
- end
-
- varargout = {runs, nbRuns};
-
- case 'filename'
-
- [session, run, type] = deal(varargin{:});
-
- fileName = spm_BIDS(BIDS, 'data', ...
- 'sub', subID, ...
- 'run', run, ...
- 'ses', session, ...
- 'task', opt.taskName, ...
- 'type', type);
-
- varargout = {fileName};
-
- otherwise
- error('Not sure what info you want me to get.');
-
- end
+ % wrapper function to fetch specific info in a BIDS structure returned by
+ % spm_bids. ::
+ %
+ % varargout = getInfo(BIDS, subID, opt, info, varargin)
+ %
+ % for a given BIDS data set, subject identity, and info type,
+ %
+ % if info = Sessions, this returns name of the sessions and their number
+ %
+ % if info = Runs, this returns name of the runs and their number for an specified session.
+ %
+ % if info = Filename, this returns the name of the file for an specified
+ % session and run.
+ %
+ % :param BIDS: (structure) returned by spm_BIDS when exploring a BIDS data set.
+ % :param subID: ID of the subject
+ % :param opt: (structure) Mostly used to find the task name.
+ % :param info: (strint) ``sessions``, ``runs``, ``filename``.
+ % :param varargin: see below
+ %
+ % - subID - ID of the subject ; in BIDS lingo that means that for a file name
+ % ``sub-02_task-foo_bold.nii`` the subID will be the string ``02``
+ % - session - ID of the session of interes ; in BIDS lingo that means that for a file name
+ % ``sub-02_ses-pretest_task-foo_bold.nii`` the sesssion will be the string
+ % ``pretest``
+ % - run: ID of the run of interest
+ % - type - string ; modality type to look for. For example: ``bold``, ``events``,
+ % ``stim``, ``physio``
+ %
+
+ varargout = {}; %#ok<*NASGU>
+
+ switch lower(info)
+
+ case 'sessions'
+
+ sessions = spm_BIDS(BIDS, 'sessions', ...
+ 'sub', subID, ...
+ 'task', opt.taskName);
+ nbSessions = size(sessions, 2);
+ if nbSessions == 0
+ nbSessions = 1;
+ sessions = {''};
+ end
+
+ varargout = {sessions, nbSessions};
+
+ case 'runs'
+
+ session = varargin{1};
+
+ runs = spm_BIDS(BIDS, 'runs', ...
+ 'sub', subID, ...
+ 'task', opt.taskName, ...
+ 'ses', session, ...
+ 'type', 'bold');
+ nbRuns = size(runs, 2); % Get the number of runs
+
+ if nbRuns == 0
+ nbRuns = 1;
+ runs = {''};
+ end
+
+ varargout = {runs, nbRuns};
+
+ case 'filename'
+
+ [session, run, type] = deal(varargin{:});
+
+ 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.');
+
+ end
end
diff --git a/src/getMeanFuncFilename.m b/src/getMeanFuncFilename.m
new file mode 100644
index 00000000..63933da4
--- /dev/null
+++ b/src/getMeanFuncFilename.m
@@ -0,0 +1,25 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function [meanImage, meanFuncDir] = getMeanFuncFilename(BIDS, subID, opt)
+ % [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt)
+ %
+ % Get the filename and the directory of an anat file for a given session /
+ % run.
+ % Unzips the file if necessary.
+
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
+ runs = getInfo(BIDS, subID, opt, 'Runs', sessions{1});
+ [boldFileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{1}, runs{1}, opt);
+
+ prefix = getPrefix('smoothing_space-individual', opt);
+
+ meanImage = validationInputFile( ...
+ subFuncDataDir, ...
+ boldFileName, ...
+ ['mean' prefix]);
+
+ [meanFuncDir, meanImage, ext] = spm_fileparts(meanImage);
+ meanImage = [meanImage ext];
+end
diff --git a/src/getPrefix.m b/src/getPrefix.m
index a8c0f758..8e6dbe15 100644
--- a/src/getPrefix.m
+++ b/src/getPrefix.m
@@ -1,82 +1,83 @@
-function [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM)
- % generates prefix to append to file name to look for
-
- prefix = '';
- motionRegressorPrefix = '';
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
- allowedPrefixCases = {
- 'STC'; ...
- 'preprocess'; ...
- 'smoothing_space-T1w'; ...
- 'smoothing'; ...
- 'FFX_space-T1w'; ...
- 'FFX'};
-
- switch step
+function [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM)
+ % [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM)
+ %
+ % generates prefix to append to file name to look for
- case 'STC'
+ if nargin < 3
+ funcFWHM = 0;
+ end
- case 'preprocess'
- prefix = prefixForSTC(prefix, opt);
+ prefix = '';
+ motionRegressorPrefix = '';
- % when smoothing is done on non-normalized data (in the space of T1w)
- case 'smoothing_space-T1w'
- prefix = prefixForSTC(prefix, opt);
- prefix = [spm_get_defaults('realign.write.prefix') prefix];
+ allowedPrefixCases = {
+ 'STC'; ...
+ 'preprocess'; ...
+ 'smoothing_space-individual'; ...
+ 'smoothing'; ...
+ 'FFX_space-individual'; ...
+ 'FFX'};
- case 'smoothing'
- prefix = prefixForSTC(prefix, opt);
- prefix = [spm_get_defaults('normalise.write.prefix') prefix];
+ switch step
- case 'FFX_space-T1w'
- prefix = prefixForSTC(prefix, opt);
+ case 'STC'
- % for the motion regressors txt file
- motionRegressorPrefix = prefix;
+ case 'preprocess'
+ prefix = prefixForSTC(prefix, opt);
- prefix = [spm_get_defaults('realign.write.prefix') prefix];
+ % when smoothing is done on non-normalized data (in the space of T1w)
+ case 'smoothing_space-individual'
+ prefix = prefixForSTC(prefix, opt);
+ prefix = [spm_get_defaults('unwarp.write.prefix') prefix];
- % Check which level of smoothing is applied
- if funcFWHM > 0 % else, take the smoothed files
- prefix = [spm_get_defaults('smooth.prefix') num2str(funcFWHM) prefix];
- end
+ case 'smoothing'
+ prefix = prefixForSTC(prefix, opt);
+ prefix = [spm_get_defaults('normalise.write.prefix') prefix];
- case 'FFX'
- prefix = prefixForSTC(prefix, opt);
+ case 'FFX_space-individual'
+ prefix = prefixForSTC(prefix, opt);
+ prefix = [spm_get_defaults('unwarp.write.prefix') prefix];
- % for the motion regressors txt file
- motionRegressorPrefix = prefix;
+ % Check which level of smoothing is applied
+ if funcFWHM > 0 % else, take the smoothed files
+ prefix = [spm_get_defaults('smooth.prefix') num2str(funcFWHM) prefix];
+ end
- prefix = [spm_get_defaults('normalise.write.prefix') prefix];
+ case 'FFX'
+ prefix = prefixForSTC(prefix, opt);
+ prefix = [spm_get_defaults('normalise.write.prefix') prefix];
- % Check which level of smoothing is applied
- if funcFWHM > 0
- prefix = [spm_get_defaults('smooth.prefix') num2str(funcFWHM) prefix];
- end
+ % Check which level of smoothing is applied
+ if funcFWHM > 0
+ prefix = [spm_get_defaults('smooth.prefix') num2str(funcFWHM) prefix];
+ end
- otherwise
+ otherwise
- fprintf(1, '\nAllowed prefix cases:\n');
- for iCase = 1:numel(allowedPrefixCases)
- fprintf(1, '- %s\n', allowedPrefixCases{iCase});
- end
+ fprintf(1, '\nAllowed prefix cases:\n');
+ for iCase = 1:numel(allowedPrefixCases)
+ fprintf(1, '- %s\n', allowedPrefixCases{iCase});
+ end
- errorStruct.identifier = 'getPrefix:unknownPrefixCase';
- errorStruct.message = sprintf('%s\n%s', ...
- 'This prefix case you have requested does not exist: %s.', ...
- 'See allowed cases above', step);
- error(errorStruct);
+ errorStruct.identifier = 'getPrefix:unknownPrefixCase';
+ errorStruct.message = sprintf('%s\n%s', ...
+ ['This prefix case you have requested ' ...
+ 'does not exist: %s.'], ...
+ 'See allowed cases above', step);
+ error(errorStruct);
- end
+ end
end
function prefix = prefixForSTC(prefix, opt)
- % Check the slice timing information is not in the metadata and not added
- % manually in the opt variable.
- if (isfield(opt.metadata, 'SliceTiming') && ...
- ~isempty(opt.metadata.SliceTiming)) || ...
- ~isempty(opt.sliceOrder)
- prefix = [spm_get_defaults('slicetiming.prefix') prefix];
- end
+ % Check the slice timing information is not in the metadata and not added
+ % manually in the opt variable.
+ if (isfield(opt.metadata, 'SliceTiming') && ...
+ ~isempty(opt.metadata.SliceTiming)) || ...
+ ~isempty(opt.sliceOrder)
+ prefix = [spm_get_defaults('slicetiming.prefix') prefix];
+ end
end
diff --git a/src/getRFXdir.m b/src/getRFXdir.m
deleted file mode 100644
index ddf08c40..00000000
--- a/src/getRFXdir.m
+++ /dev/null
@@ -1,21 +0,0 @@
-function rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName)
- % rfxDir = getRFXdir(opt, funcFWHM, conFWHM, iStep, iCon)
- %
- % sets the name the RFX directory and creates it if it does not exist
- %
-
- rfxDir = fullfile( ...
- opt.dataDir, ...
- '..', ...
- 'derivatives', ...
- 'SPM12_CPPL', ...
- 'group', ...
- ['rfx_task-', opt.taskName], ...
- ['rfx_funcFWHM-', num2str(funcFWHM), '_conFWHM-', num2str(conFWHM)], ...
- contrastName);
-
- if ~exist(rfxDir, 'dir')
- mkdir(rfxDir);
- end
-
-end
diff --git a/src/getRealignParamFile.m b/src/getRealignParamFile.m
index 88215a8e..85182a30 100644
--- a/src/getRealignParamFile.m
+++ b/src/getRealignParamFile.m
@@ -1,21 +1,14 @@
-function realignParamFile = getRealignParamFile(opt, fullpathBoldFileName, funcFWHM)
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
- [prefix, motionRegressorPrefix] = getPrefix('FFX', opt, funcFWHM);
- if strcmp(opt.space, 'T1w')
- [prefix, motionRegressorPrefix] = getPrefix('FFX_space-T1w', opt, funcFWHM);
- end
+function realignParamFile = getRealignParamFile(fullpathBoldFileName, prefix)
- [funcDataDir, boldFileName] = spm_fileparts(fullpathBoldFileName{1});
+ [funcDataDir, boldFileName] = spm_fileparts(fullpathBoldFileName);
- realignParamFile = strrep(boldFileName, prefix, motionRegressorPrefix);
- realignParamFile = ['rp_', realignParamFile, '.txt'];
- realignParamFile = fullfile(funcDataDir, realignParamFile);
+ if nargin > 1
+ boldFileName = strrep(boldFileName, [prefix 'sub-'], 'sub-');
+ end
+
+ realignParamFile = ['rp_.*' boldFileName, '.txt'];
+ realignParamFile = validationInputFile(funcDataDir, realignParamFile);
- if ~exist(realignParamFile, 'file')
- errorStruct.identifier = 'getRealignParamFile:nonExistentFile';
- errorStruct.message = sprintf('%s\n%s', ...
- 'This realignment file does not exist:', ...
- realignParamFile);
- error(errorStruct);
- end
end
diff --git a/src/getSliceOrder.m b/src/getSliceOrder.m
index c863d825..7671a206 100644
--- a/src/getSliceOrder.m
+++ b/src/getSliceOrder.m
@@ -1,52 +1,54 @@
-function sliceOrder = getSliceOrder(opt, verbose)
- % get the slice order information from the BIDS data set or from getOption
- %
- % In the case the slice timing information was not specified in the json FILES
- % in the BIDS data set then it will try to read the opt structure for any relevant information.
- % If this comes out empty then slice timing correction will be skipped.
-
- if nargin < 2
- verbose = 0;
- end
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
- msg = {};
- wng = {};
+function sliceOrder = getSliceOrder(opt, verbose)
+ % get the slice order information from the BIDS data set or from getOption
+ %
+ % In the case the slice timing information was not specified in the json FILES
+ % in the BIDS data set then it will try to read the opt structure for any relevant information.
+ % If this comes out empty then slice timing correction will be skipped.
- % IF slice timing is not in the metadata
- if ~isfield(opt.metadata, 'SliceTiming') || isempty(opt.metadata.SliceTiming)
+ if nargin < 2
+ verbose = 0;
+ end
- msg{end + 1} = ' SLICE TIMING INFORMATION COULD NOT BE EXTRACTED FROM METADATA.\n';
- msg{end + 1} = ' CHECKING IF SPECIFIED IN opt IN THE "getOption" FUNCTION.\n\n';
+ msg = {};
+ wng = {};
- % IF SLICE TIME information is not in the metadata, you have the option
- % to add the slice order manually in the "opt" in the "getOptions"
- % function
- if ~isempty(opt.sliceOrder)
- sliceOrder = opt.sliceOrder;
+ % IF slice timing is not in the metadata
+ if ~isfield(opt.metadata, 'SliceTiming') || isempty(opt.metadata.SliceTiming)
- msg{end + 1} = ' SLICE TIMING INFORMATION EXTRACTED FROM OPTIONS.\n\n';
+ msg{end + 1} = ' SLICE TIMING INFORMATION COULD NOT BE EXTRACTED FROM METADATA.\n';
+ msg{end + 1} = ' CHECKING IF SPECIFIED IN opt IN THE "getOption" FUNCTION.\n\n';
- else
+ % IF SLICE TIME information is not in the metadata, you have the option
+ % to add the slice order manually in the "opt" in the "getOptions"
+ % function
+ if ~isempty(opt.sliceOrder)
+ sliceOrder = opt.sliceOrder;
- msg{end + 1} = ' SLICE TIMING INFORMATION COULD NOT BE EXTRACTED.\n';
- wng{end + 1} = 'SKIPPING SLICE TIME CORRECTION: no slice timing specified.\n\n';
+ msg{end + 1} = ' SLICE TIMING INFORMATION EXTRACTED FROM OPTIONS.\n\n';
- sliceOrder = [];
- end
- else % Otherwise get the slice order from the metadata
- sliceOrder = opt.metadata.SliceTiming;
+ else
- msg{end + 1} = ' SLICE TIMING INFORMATION EXTRACTED FROM METADATA.\n\n';
+ msg{end + 1} = ' SLICE TIMING INFORMATION COULD NOT BE EXTRACTED.\n';
+ wng{end + 1} = 'SKIPPING SLICE TIME CORRECTION: no slice timing specified.\n\n';
+ sliceOrder = [];
end
+ else % Otherwise get the slice order from the metadata
+ sliceOrder = opt.metadata.SliceTiming;
- if verbose
- for iMsg = 1:numel(msg)
- fprintf(1, msg{iMsg});
- end
- for iWng = 1:numel(wng)
- warning(wng{iWng});
- end
+ msg{end + 1} = ' SLICE TIMING INFORMATION EXTRACTED FROM METADATA.\n\n';
+
+ end
+
+ if verbose
+ for iMsg = 1:numel(msg)
+ fprintf(1, msg{iMsg});
+ end
+ for iWng = 1:numel(wng)
+ warning(wng{iWng});
end
+ end
end
diff --git a/src/getSpecificSubjects.m b/src/getSpecificSubjects.m
new file mode 100644
index 00000000..190a48bd
--- /dev/null
+++ b/src/getSpecificSubjects.m
@@ -0,0 +1,45 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function group = getSpecificSubjects(opt, group, iGroup, subjects)
+
+ % add a test for ata set with subject only blind02 and we ask for this one
+ % specifically
+
+ % if no group or subject was specified we take all of them
+ if numel(opt.groups) == 1 && ...
+ strcmp(group(iGroup).name, '') && ...
+ isempty(opt.subjects{iGroup})
+
+ group(iGroup).subNumber = subjects;
+
+ % if subject ID were directly specified by users we take those
+ elseif strcmp(group(iGroup).name, '') && iscellstr(opt.subjects)
+
+ group(iGroup).subNumber = opt.subjects;
+
+ % if group was specified we figure out which subjects to take
+ elseif ~isempty(opt.subjects{iGroup})
+
+ idx = opt.subjects{iGroup};
+
+ % else we take all subjects of that group
+ elseif isempty(opt.subjects{iGroup})
+
+ % count how many subjects in that group
+ idx = sum(~cellfun(@isempty, strfind(subjects, group(iGroup).name)));
+ idx = 1:idx;
+
+ else
+
+ error('Not sure what to do.');
+
+ end
+
+ % if only indices were specified we get the subject from that group with that
+ if exist('idx', 'var')
+ pattern = [group(iGroup).name '%0' num2str(opt.zeropad) '.0f_'];
+ temp = strsplit(sprintf(pattern, idx), '_');
+ group(iGroup).subNumber = temp(1:end - 1);
+ end
+
+end
diff --git a/src/group_level/getGrpLevelContrastToCompute.m b/src/group_level/getGrpLevelContrastToCompute.m
new file mode 100644
index 00000000..1833e6fa
--- /dev/null
+++ b/src/group_level/getGrpLevelContrastToCompute.m
@@ -0,0 +1,14 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function [grpLvlCon, iStep] = getGrpLevelContrastToCompute(opt)
+
+ model = spm_jsonread(opt.model.file);
+
+ for iStep = 1:length(model.Steps)
+ if strcmp(model.Steps{iStep}.Level, 'dataset')
+ grpLvlCon = model.Steps{iStep}.AutoContrasts;
+ break
+ end
+ end
+
+end
diff --git a/src/group_level/getRFXdir.m b/src/group_level/getRFXdir.m
new file mode 100644
index 00000000..052fdbe1
--- /dev/null
+++ b/src/group_level/getRFXdir.m
@@ -0,0 +1,20 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName)
+ % rfxDir = getRFXdir(opt, funcFWHM, conFWHM, iStep, iCon)
+ %
+ % sets the name the RFX directory and creates it if it does not exist
+ %
+
+ rfxDir = fullfile( ...
+ opt.derivativesDir, ...
+ 'group', ...
+ ['rfx_task-', opt.taskName], ...
+ ['rfx_funcFWHM-', num2str(funcFWHM), '_conFWHM-', num2str(conFWHM)], ...
+ contrastName);
+
+ if ~exist(rfxDir, 'dir')
+ mkdir(rfxDir);
+ end
+
+end
diff --git a/src/mancoreg/mancoreg.m b/src/mancoreg/mancoreg.m
new file mode 100644
index 00000000..717decd0
--- /dev/null
+++ b/src/mancoreg/mancoreg.m
@@ -0,0 +1,382 @@
+% (C) Copyright 2004-2009 JH
+% (C) Copyright 2009_2012 DSS
+% (C) Copyright 2012_2019 Remi Gau
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function mancoreg(targetimage, sourceimage)
+ % mancoreg(targetimage, sourceimage)
+ %
+ % Manual coregistration tool
+ %
+ % FORMAT mancoreg(targetimage,sourceimage);
+ %
+ % This function displays 2 ortho views of a TARGET and
+ % a SOURCE image that can be manually coregistered.
+ %
+ % The source image (bottom graph) can be manually
+ % rotated and translated with 6 slider controls. In the source
+ % graph the source image can be exchanged with the target image
+ % using a radio button toggle. This is helpful for visual fine control
+ % of the coregistration. The transformation matrix can be applied
+ % to a selected set of volumes with the "apply transformation" button.
+ % If the transformation is to be applied to the original source file
+ % that file will also need to be selected. If the sourceimage or
+ % targetimage are not passed the user will be prompted with a file browser.
+ %
+ % The code is loosely based on spm_image.m and spm_orthoviews.m
+ % It requires the m-file with the callback functions for the user
+ % controls (mancoregCallbacks.m).
+
+ % JH 10.01.2004
+ % modified DSS 10/02/2009
+ %
+ % Change LOG
+ %
+ % Version 1.0.1
+ % Radio button cannot be turned off on matlab for linux (6.5.0). Changed to
+ % two radio buttons for toggle on/off (12.1.2004, JH)
+ %
+ % Version 1.0.2
+ % Added: Plot of transformation matrix, values are shown next to sliders
+ % and "reset transformation" button (12.1.2004, JH)
+ %
+ % Version 1.0.3
+ % Added: Made compatible with SPM5 and corrected "Yawn" (altho I like it)
+
+ global st mancoregvar
+
+ opt.smallFontSize = 8;
+ opt.largeFontSize = 10;
+
+ opt.smallFontBoxHeight = 15;
+ opt.largeFontBoxHeight = 19;
+
+ opt.smallFontBoxWidth = 40;
+ opt.largeFontBoxWidth = 60;
+
+ opt.translationSliderMin = -500;
+ opt.translationSliderMax = 500;
+
+ opt.rotationSliderMin = -pi;
+ opt.rotationSliderMax = pi;
+
+ opt.sliderStep = [0.01 0.01];
+
+ %% Make sure we have both image filenames and map images
+
+ if ~exist('targetimage', 'var')
+ targetimage = spm_select(1, 'image', 'Please select target image');
+ end
+
+ if ~exist('sourceimage', 'var')
+ sourceimage = spm_select(1, 'image', 'Please select source image');
+ end
+
+ targetvol = spm_vol(targetimage);
+ sourcevol = spm_vol(sourceimage);
+
+ %% Init graphics window
+
+ fg = spm_figure('Findwin', 'Graphics');
+ if isempty(fg)
+ fg = spm_figure('Create', 'Graphics');
+ if isempty(fg)
+ error('Cant create graphics window');
+ end
+ else
+ spm_figure('Clear', 'Graphics');
+ end
+ opt.windowScale = spm('WinScale');
+
+ htargetstim = spm_orthviews('Image', targetvol, [0.1 0.55 0.45 0.48]);
+ spm_orthviews('space');
+ spm_orthviews('AddContext', htargetstim);
+
+ hsourcestim = spm_orthviews('Image', sourcevol, [0.1 0.08 0.45 0.48]);
+ spm_orthviews('AddContext', hsourcestim);
+
+ spm_orthviews('maxbb');
+
+ mancoregvar.targetimage = st.vols{1};
+ mancoregvar.sourceimage = st.vols{2};
+
+ %% Define and initialise all user controls
+
+ initTitleAndBoxes(opt, fg, targetimage, sourceimage);
+ mancoregvar = initTransMat(mancoregvar, opt, fg);
+ mancoregvar = initRotationSlider(mancoregvar, opt, fg);
+ mancoregvar = initTranslationSlider(mancoregvar, opt, fg);
+
+ % Source/target display toggle
+
+ mancoregvar.htoggle_off = uicontrol(fg, ...
+ 'style', 'radiobutton', ...
+ 'position', ...
+ [470 100 opt.smallFontBoxWidth * ...
+ 2 opt.largeFontBoxHeight] .* opt.windowScale, ...
+ 'Value', 1, ...
+ 'Callback', 'mancoregCallbacks(''toggle_off'')', ...
+ 'string', 'OFF');
+
+ mancoregvar.htoggle_on = uicontrol(fg, ...
+ 'style', 'radiobutton', ...
+ 'position', ...
+ [530 100 opt.largeFontBoxWidth opt.largeFontBoxHeight] .* ...
+ opt.windowScale, ...
+ 'Value', 0, ...
+ 'Callback', 'mancoregCallbacks(''toggle_on'')', ...
+ 'string', 'ON');
+
+ % "Reset transformation" pushbutton
+
+ mancoregvar.hreset = uicontrol(fg, ...
+ 'style', 'pushbutton', ...
+ 'position', [370 75 220 opt.largeFontBoxHeight] .* ...
+ opt.windowScale, ...
+ 'String', 'Reset transformation', ...
+ 'Callback', 'mancoregCallbacks(''reset'')');
+
+ % "Apply transformation" pushbutton
+
+ mancoregvar.hwrite = uicontrol(fg, 'style', 'pushbutton', ...
+ 'position', [370 50 220 opt.largeFontBoxHeight] .* ...
+ opt.windowScale, ...
+ 'String', 'Apply transformation', ...
+ 'Callback', 'mancoregCallbacks(''apply'')');
+
+ %% Fill in "transf." fields
+
+ mancoregCallbacks('plotmat');
+
+ return
+
+end
+
+function initTitleAndBoxes(opt, fg, targetimage, sourceimage)
+
+ windowScale = opt.windowScale;
+ fontSize = opt.largeFontSize;
+ height = opt.largeFontBoxHeight;
+
+ uicontrol(fg, ...
+ 'style', 'text', ...
+ 'string', 'Manual coregistration tool', ...
+ 'position', [200 825 300 30] .* windowScale, ...
+ 'Fontsize', 16, 'backgroundcolor', [1 1 1]);
+
+ uicontrol(fg, ...
+ 'style', 'frame', ...
+ 'position', [360 550 240 250] .* windowScale);
+ uicontrol(fg, 'style', 'frame', ...
+ 'position', [360 40 240 410] .* windowScale);
+
+ addTextToUI( ...
+ fg, 'TARGET IMAGE', ...
+ [370 760 100 height], opt, fontSize);
+ addTextToUI( ...
+ fg, targetimage, ...
+ [370 700 220 height * 3], opt, fontSize);
+
+ addTextToUI( ...
+ fg, 'SOURCE IMAGE', ...
+ [370 415 100 height], opt, fontSize);
+ addTextToUI( ...
+ fg, sourceimage, ...
+ [370 395 220 height], opt, fontSize);
+
+end
+
+function mancoregvar = initTransMat(mancoregvar, opt, fg)
+
+ fontSize = opt.smallFontSize;
+
+ width = opt.smallFontBoxWidth;
+ height = opt.smallFontBoxHeight;
+
+ colPosition = 415:45:550;
+ rowPosition = 360:-20:300;
+
+ addTextToUI( ...
+ fg, 'transf.', ...
+ [370 360 width height], opt, opt.largeFontSize);
+
+ for iRow = 1:numel(rowPosition)
+
+ for iCol = 1:numel(colPosition)
+
+ xPos = colPosition(iCol);
+ yPos = rowPosition(iRow);
+ fieldName = sprintf('hmat_%i_%i', iRow, iCol);
+
+ stringToUse = '0.00';
+ if iRow == iCol
+ stringToUse = '1.00';
+ end
+
+ mancoregvar = addTextToUI( ...
+ fg, stringToUse, ...
+ [xPos yPos width height], opt, fontSize, ...
+ mancoregvar, fieldName);
+
+ end
+ end
+
+end
+
+function mancoregvar = initRotationSlider(mancoregvar, opt, fg)
+
+ windowScale = opt.windowScale;
+ fontSize = opt.largeFontSize;
+ height = opt.largeFontBoxHeight;
+ width = opt.largeFontBoxWidth;
+
+ sliderMin = opt.rotationSliderMin;
+ sliderMax = opt.rotationSliderMax;
+
+ sliderStep = opt.sliderStep;
+
+ % set sliders
+ mancoregvar.hpitch = uicontrol(fg, 'style', 'slider', ...
+ 'position', [430 250 100 height] .* windowScale, ...
+ 'Value', 0, ...
+ 'min', sliderMin, ...
+ 'max', sliderMax, ...
+ 'sliderstep', sliderStep, ...
+ 'Callback', 'mancoregCallbacks(''move'')');
+ mancoregvar.hroll = uicontrol(fg, 'style', 'slider', ...
+ 'position', [430 225 100 height] .* windowScale, ...
+ 'Value', 0, ...
+ 'min', sliderMin, ...
+ 'max', sliderMax, ...
+ 'sliderstep', sliderStep, ...
+ 'Callback', 'mancoregCallbacks(''move'')');
+ mancoregvar.hyaw = uicontrol(fg, 'style', 'slider', ...
+ 'position', [430 200 100 height] .* windowScale, ...
+ 'Value', 0, ...
+ 'min', sliderMin, ...
+ 'max', sliderMax, ...
+ 'sliderstep', sliderStep, ...
+ 'Callback', 'mancoregCallbacks(''move'')');
+
+ % display text
+ xPos = 370;
+
+ addTextToUI( ...
+ fg, 'PITCH', ...
+ [xPos 250 width height], opt, fontSize);
+ addTextToUI( ...
+ fg, 'ROLL', ...
+ [xPos 225 width height], opt, fontSize);
+ addTextToUI( ...
+ fg, 'YAW', ...
+ [xPos 200 width height], opt, fontSize);
+
+ % display value
+ xPos = 530;
+
+ mancoregvar = addTextToUI( ...
+ fg, '0', ...
+ [xPos 250 width height], opt, fontSize, ...
+ mancoregvar, 'hpitch_val');
+ mancoregvar = addTextToUI( ...
+ fg, '0', ...
+ [xPos 225 width height], opt, fontSize, ...
+ mancoregvar, 'hroll_val');
+ mancoregvar = addTextToUI( ...
+ fg, '0', ...
+ [xPos 200 width height], opt, fontSize, ...
+ mancoregvar, 'hyaw_val');
+
+end
+
+function mancoregvar = initTranslationSlider(mancoregvar, opt, fg)
+
+ windowScale = opt.windowScale;
+ fontSize = opt.largeFontSize;
+ height = opt.largeFontBoxHeight;
+ width = opt.largeFontBoxWidth;
+
+ sliderMin = opt.translationSliderMin;
+ sliderMax = opt.translationSliderMax;
+
+ sliderStep = opt.sliderStep;
+
+ % add sliders
+ xPos = 430;
+
+ mancoregvar.hx = uicontrol(fg, ...
+ 'style', 'slider', ...
+ 'position', [xPos 175 100 height] .* windowScale, ...
+ 'Value', 0, ...
+ 'min', sliderMin, ...
+ 'max', sliderMax, ...
+ 'sliderstep', sliderStep, ...
+ 'Callback', 'mancoregCallbacks(''move'')');
+ mancoregvar.hy = uicontrol(fg, ...
+ 'style', 'slider', ...
+ 'position', [xPos 150 100 height] .* windowScale, ...
+ 'Value', 0, ...
+ 'min', sliderMin, ...
+ 'max', sliderMax, ...
+ 'sliderstep', sliderStep, ...
+ 'Callback', 'mancoregCallbacks(''move'')');
+ mancoregvar.hz = uicontrol(fg, ...
+ 'style', 'slider', ...
+ 'position', [xPos 125 100 height] .* windowScale, ...
+ 'Value', 0, ...
+ 'min', sliderMin, ...
+ 'max', sliderMax, ...
+ 'sliderstep', sliderStep, ...
+ 'Callback', 'mancoregCallbacks(''move'')');
+
+ % display text
+ xPos = 370;
+
+ addTextToUI( ...
+ fg, 'X', ...
+ [xPos 175 width height], opt, fontSize);
+ addTextToUI( ...
+ fg, 'Y', ...
+ [xPos 150 width height], opt, fontSize);
+ addTextToUI( ...
+ fg, 'Z', ...
+ [xPos 125 width height], opt, fontSize);
+
+ % display value
+ xPos = 530;
+
+ mancoregvar = addTextToUI( ...
+ fg, '0', ...
+ [xPos 175 width height], opt, fontSize, ...
+ mancoregvar, 'hx_val');
+ mancoregvar = addTextToUI( ...
+ fg, '0', ...
+ [xPos 150 width height], opt, fontSize, ...
+ mancoregvar, 'hy_val');
+ mancoregvar = addTextToUI( ...
+ fg, '0', ...
+ [xPos 125 width height], opt, fontSize, ...
+ mancoregvar, 'hz_val');
+
+end
+
+function mancoregvar = addTextToUI(varargin)
+
+ if numel(varargin) == 7
+ [fg, textString, position, opt, fontSize, mancoregvar, fieldName] = deal(varargin{:});
+ elseif numel(varargin) == 5
+ [fg, textString, position, opt, fontSize] = deal(varargin{:});
+ fieldName = [];
+ end
+
+ handle = uicontrol(fg, ...
+ 'style', 'text', ...
+ 'string', textString, ...
+ 'position', position .* opt.windowScale, ...
+ 'Fontsize', fontSize);
+
+ if ~isempty(fieldName)
+ mancoregvar.(fieldName) = handle;
+ end
+
+end
diff --git a/src/mancoreg/mancoregCallbacks.m b/src/mancoreg/mancoregCallbacks.m
new file mode 100644
index 00000000..9257e85d
--- /dev/null
+++ b/src/mancoreg/mancoregCallbacks.m
@@ -0,0 +1,254 @@
+% (C) Copyright 2004-2009 JH
+% (C) Copyright 2009_2012 DSS
+% (C) Copyright 2012_2019 Remi Gau
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function mancoregCallbacks(operation, varargin)
+ % mancoreg_callbacks(op, varargin)
+ %
+ % Callback routines for mancoreg.m
+
+ % Change LOG
+ %
+ % Version 1.0.1
+ % Radio button cannot be turned off on matlab for linux (6.5.0). Changed to
+ % two radio buttons for toggle on/off (12.1.2004, JH)
+ %
+ % Version 1.0.2
+ % Added: Plot of transformation matrix, values are shown next to sliders
+ % and "reset transformation" button (12.1.2004, JH)
+ %
+
+ switch operation
+
+ case 'move'
+ % Update the position of the bottom (source) image according to user settings
+
+ moveImage();
+
+ % Toggles between source and target display in bottom window
+ case 'toggle_off'
+ toggleOff();
+ case 'toggle_on'
+ toggleOn();
+
+ case 'reset'
+ resetTransformationMatrix();
+
+ case 'apply'
+
+ applyTransformationMatrix();
+
+ case 'plotmat'
+ % Plot matrix notation of transformation
+
+ plotMat;
+
+ otherwise
+ % None of the op strings matches
+
+ fprintf('WARNING: mancoreg_callbacks.m called with unspecified operation!\n');
+
+ end
+
+end
+
+function moveImage()
+
+ global st mancoregvar;
+
+ angl_pitch = get(mancoregvar.hpitch, 'Value');
+ angl_roll = get(mancoregvar.hroll, 'Value');
+ angl_yaw = get(mancoregvar.hyaw, 'Value');
+
+ dist_x = get(mancoregvar.hx, 'Value');
+ dist_y = get(mancoregvar.hy, 'Value');
+ dist_z = get(mancoregvar.hz, 'Value');
+
+ set(mancoregvar.hpitch_val, 'string', num2str(angl_pitch));
+ set(mancoregvar.hroll_val, 'string', num2str(angl_roll));
+ set(mancoregvar.hyaw_val, 'string', num2str(angl_yaw));
+
+ set(mancoregvar.hx_val, 'string', num2str(dist_x));
+ set(mancoregvar.hy_val, 'string', num2str(dist_y));
+ set(mancoregvar.hz_val, 'string', num2str(dist_z));
+
+ mancoregvar.sourceimage.premul = ...
+ spm_matrix([dist_x dist_y dist_z angl_pitch angl_roll angl_yaw 1 1 1 0 0 0]);
+ if get(mancoregvar.htoggle_on, 'value') == 0 % source is currently displayed
+ st.vols{2}.premul = ...
+ spm_matrix([dist_x dist_y dist_z angl_pitch angl_roll angl_yaw 1 1 1 0 0 0]);
+ end
+
+ plotMat;
+ spm_orthviews('redraw');
+
+end
+
+function toggleOff()
+
+ global st mancoregvar;
+
+ if get(mancoregvar.htoggle_off, 'value') == 0 % Source is to be displayed
+
+ set(mancoregvar.htoggle_off, 'value', 1);
+
+ else
+
+ set(mancoregvar.htoggle_on, 'value', 0);
+ st.vols{2} = mancoregvar.sourceimage;
+ spm_orthviews('redraw');
+
+ end
+
+end
+
+function toggleOn()
+
+ global st mancoregvar;
+
+ if get(mancoregvar.htoggle_on, 'value') == 0 % Source is to be displayed
+
+ set(mancoregvar.htoggle_on, 'value', 1);
+
+ else
+ set(mancoregvar.htoggle_off, 'value', 0);
+ mancoregvar.sourceimage = st.vols{2}; % Backup current state
+ st.vols{2} = st.vols{1};
+ st.vols{2}.ax = mancoregvar.sourceimage.ax; % These have to stay the same
+ st.vols{2}.window = mancoregvar.sourceimage.window;
+ st.vols{2}.area = mancoregvar.sourceimage.area;
+ spm_orthviews('redraw');
+ end
+
+end
+
+function resetTransformationMatrix()
+
+ global st mancoregvar;
+
+ set(mancoregvar.hpitch, 'Value', 0);
+ set(mancoregvar.hroll, 'Value', 0);
+ set(mancoregvar.hyaw, 'Value', 0);
+
+ set(mancoregvar.hx, 'Value', 0);
+ set(mancoregvar.hy, 'Value', 0);
+ set(mancoregvar.hz, 'Value', 0);
+
+ set(mancoregvar.hpitch_val, 'string', '0');
+ set(mancoregvar.hroll_val, 'string', '0');
+ set(mancoregvar.hyaw_val, 'string', '0');
+
+ set(mancoregvar.hx_val, 'string', '0');
+ set(mancoregvar.hy_val, 'string', '0');
+ set(mancoregvar.hz_val, 'string', '0');
+
+ mancoregvar.sourceimage.premul = spm_matrix([0 0 0 0 0 0 1 1 1 0 0 0]);
+ if get(mancoregvar.htoggle_on, 'value') == 0 % source is currently displayed
+ st.vols{2}.premul = spm_matrix([0 0 0 0 0 0 1 1 1 0 0 0]);
+ end
+
+ plotMat;
+ spm_orthviews('redraw');
+
+end
+
+function applyTransformationMatrix()
+
+ global st mancoregvar;
+
+ angl_pitch = get(mancoregvar.hpitch, 'Value');
+ angl_roll = get(mancoregvar.hroll, 'Value');
+ angl_yaw = get(mancoregvar.hyaw, 'Value');
+
+ dist_x = get(mancoregvar.hx, 'Value');
+ dist_y = get(mancoregvar.hy, 'Value');
+ dist_z = get(mancoregvar.hz, 'Value');
+ spm_defaults;
+ reorientationMatrix = ...
+ spm_matrix([dist_x dist_y dist_z angl_pitch angl_roll angl_yaw 1 1 1 0 0 0]);
+
+ % The following is copied from spm_image.m
+ if det(reorientationMatrix) <= 0
+ spm('alert!', 'This will flip the images', mfilename, 0, 1);
+ end
+ imagesToReorient = spm_select(Inf, 'image', 'Images to reorient');
+
+ saveReorientationMatrix(imagesToReorient, reorientationMatrix);
+
+ matricesToChange = zeros(4, 4, size(imagesToReorient, 1));
+
+ for i = 1:size(imagesToReorient, 1)
+ tmp = ...
+ sprintf('Reading current orientations... %.0f%%.\n', i / ...
+ size(imagesToReorient, 1) * 100);
+ fprintf('%s', tmp);
+
+ matricesToChange(:, :, i) = spm_get_space(imagesToReorient(i, :));
+ spm_progress_bar('Set', i);
+
+ fprintf('%s', char(sign(tmp) * 8));
+ end
+
+ for i = 1:size(imagesToReorient, 1)
+ tmp = ...
+ sprintf('Reorienting images... %.0f%%.\n', i / size(imagesToReorient, 1) * 100);
+ fprintf('%s', tmp);
+
+ spm_get_space(imagesToReorient(i, :), reorientationMatrix * matricesToChange(:, :, i));
+
+ fprintf('%s', char(sign(tmp) * 8));
+ end
+
+ tmp = spm_get_space([st.vols{1}.fname ',' num2str(st.vols{1}.n)]);
+ if sum((tmp(:) - st.vols{1}.mat(:)).^2) > 1e-8
+ spm_image('init', st.vols{1}.fname);
+ end
+end
+
+function plotMat
+
+ global mancoregvar;
+
+ angl_pitch = get(mancoregvar.hpitch, 'Value');
+ angl_roll = get(mancoregvar.hroll, 'Value');
+ angl_yaw = get(mancoregvar.hyaw, 'Value');
+
+ dist_x = get(mancoregvar.hx, 'Value');
+ dist_y = get(mancoregvar.hy, 'Value');
+ dist_z = get(mancoregvar.hz, 'Value');
+
+ premul = spm_matrix([dist_x dist_y dist_z angl_pitch angl_roll angl_yaw 1 1 1 0 0 0]);
+
+ for iRow = 1:4
+
+ for iCol = 1:4
+
+ fieldName = sprintf('hmat_%i_%i', iRow, iCol);
+ stringToUse = sprintf('%2.4g', premul(iRow, iCol));
+
+ set(mancoregvar.(fieldName), 'string', stringToUse);
+
+ end
+ end
+
+end
+
+function saveReorientationMatrix(imagesToReorient, reorientationMatrix)
+
+ dateFormat = 'yyyymmdd_HHMM';
+
+ filename = fullfile(pwd, strcat('reorientMatrix_', datestr(now, dateFormat)));
+
+ fprintf(['Saving reorient matrice to ' [filename '.mat/json'] '.\n']);
+
+ save([filename '.mat'], 'reorientationMatrix', 'imagesToReorient', '-v7');
+
+ json.reorientationMatrix = reorientationMatrix;
+ json.imagesToReorient = imagesToReorient;
+
+ opts.indent = ' ';
+
+ spm_jsonwrite([filename '.json'], json, opts);
+
+end
diff --git a/src/printProcessingRun.m b/src/printProcessingRun.m
deleted file mode 100644
index 5a7c9d81..00000000
--- a/src/printProcessingRun.m
+++ /dev/null
@@ -1,12 +0,0 @@
-function printProcessingRun(groupName, iSub, subID, iSes, iRun)
-
- fprintf(1, ...
- [ ...
- 'PROCESSING GROUP: %s' ...
- 'SUBJECT No.: %i ' ...
- 'SUBJECT ID : %s ' ...
- 'SESSION: %i ' ...
- 'RUN: %i \n'], ...
- groupName, iSub, subID, iSes, iRun);
-
-end
diff --git a/src/printProcessingSubject.m b/src/printProcessingSubject.m
deleted file mode 100644
index 1d654ba8..00000000
--- a/src/printProcessingSubject.m
+++ /dev/null
@@ -1,9 +0,0 @@
-function printProcessingSubject(groupName, iSub, subID)
-
- fprintf(1, [ ...
- 'PROCESSING GROUP: %s' ...
- 'SUBJECT No.: %i ' ...
- 'SUBJECT ID : %s \n'], ...
- groupName, iSub, subID);
-
-end
diff --git a/src/reports/copyGraphWindownOutput.m b/src/reports/copyGraphWindownOutput.m
new file mode 100644
index 00000000..88e92598
--- /dev/null
+++ b/src/reports/copyGraphWindownOutput.m
@@ -0,0 +1,61 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function imgNb = copyGraphWindownOutput(opt, subID, action, imgNb)
+ %
+ % Looks into the current directory for an ``spm_.*imgNb.png`` file and moves it into
+ % the output directory ``sub-label/figures``.
+ % The output name of the file is
+ % ``yyyymmddHHMM_sub-label_task-label_action.png``
+ %
+ % USAGE::
+ %
+ % imgNb = copyGraphWindownOutput(opt, subID, [action = '',] [imgNb = 1])
+ %
+ % :param opt: options
+ % :type opt: structure
+ % :param subID:
+ % :type subID: string
+ % :param action:
+ % :type action: string
+ % :param imgNb: image number to look for. SPM increments them automatically.
+ % :type imgNb: integer
+ %
+ % :returns: - :imgNb: (integer) number of the next image to get.
+ %
+ % assumes that no file was generated if SPM is running in command line mode
+
+ if nargin < 4 || isempty(imgNb)
+ imgNb = 1;
+ end
+
+ if nargin < 3 || isempty(action)
+ action = '';
+ end
+
+ if ~spm('CmdLine') && ~isOctave
+
+ figureDir = fullfile(opt.derivativesDir, ['sub-' subID], 'figures');
+ if ~exist(figureDir, 'dir')
+ mkdir(figureDir);
+ end
+
+ file = spm_select('FPList', pwd, sprintf('^spm_.*%i.png$', imgNb));
+
+ if ~isempty(file)
+
+ targetFile = [datestr(now, 'yyyymmddHHMM') ...
+ '_sub-', subID, ...
+ '_task-', opt.taskName, ...
+ '_' action '.png'];
+
+ movefile( ...
+ file, ...
+ fullfile(figureDir, targetFile));
+
+ imgNb = imgNb + 1;
+
+ end
+
+ end
+
+end
diff --git a/src/reports/reportBIDS.m b/src/reports/reportBIDS.m
new file mode 100644
index 00000000..4fe2adce
--- /dev/null
+++ b/src/reports/reportBIDS.m
@@ -0,0 +1,10 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function reportBIDS(opt)
+
+ bids.report(opt.dataDir);
+
+ % TODO save output in the derivatires folder
+ % derivativeDir = fullfile(rawDir, '..', 'derivatives', 'SPM12_CPPL');
+
+end
diff --git a/src/saveMatlabBatch.m b/src/saveMatlabBatch.m
deleted file mode 100644
index 30aee51b..00000000
--- a/src/saveMatlabBatch.m
+++ /dev/null
@@ -1,41 +0,0 @@
-function saveMatlabBatch(batch, batchType, opt, subID)
- % saveMatlabBatch(batch, batchType, opt, subID)
- %
- % Also save some basic environnment info
- %
- % batch : matlabbatch
- % batchType: (string) name to give to the batch file
- %
- %
-
- if nargin < 4 || isempty(subID)
- subID = 'group';
- else
- subID = ['sub-' subID];
- end
-
- jobsDir = fullfile(opt.jobsDir, subID);
- [~, ~, ~] = mkdir(jobsDir);
-
- filename = sprintf( ...
- '%s_jobs_matlabbatch_SPM12_%s.mat', ...
- datestr(now, 'yyyymmdd_HHMM'), ...
- batchType);
-
- [OS, GeneratedBy] = getEnvInfo();
- GeneratedBy(1).Description = batchType;
-
- save(fullfile(jobsDir, filename), 'batch', 'OS', 'GeneratedBy');
-
- % write as json for more "human readibility"
- opts.indent = ' ';
-
- json.matlabbach = batch;
- json.GeneratedBy = GeneratedBy;
- json.OS = OS;
-
- spm_jsonwrite( ...
- fullfile(jobsDir, strrep(filename, '.mat', '.json')), ...
- json, opts);
-
-end
diff --git a/src/setDerivativesDir.m b/src/setDerivativesDir.m
new file mode 100644
index 00000000..15d51c37
--- /dev/null
+++ b/src/setDerivativesDir.m
@@ -0,0 +1,42 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function opt = setDerivativesDir(opt)
+ % derivativeDir sets the derivatives folder
+ %
+ % opt = setDerivativesDir(opt)
+ %
+ % Parameters:
+ % opt: option structure
+ %
+ % Returns:
+ % opt: with the additional field derivativesDir
+
+ if ~isfield(opt, 'derivativesDir') || isempty(opt.derivativesDir)
+ opt.derivativesDir = fullfile(opt.dataDir, '..', 'derivatives', 'SPM12_CPPL');
+
+ end
+
+ try
+ folders = split(opt.derivativesDir, filesep);
+ catch
+ % for octave
+ folders = strsplit(opt.derivativesDir, filesep);
+ end
+
+ if ~strcmp(folders{end - 1}, 'derivatives') && ~strcmp(folders{end}, 'SPM12_CPPL')
+ folders{end + 1} = 'derivatives';
+ folders{end + 1} = 'SPM12_CPPL';
+ end
+
+ try
+ tmp = join(folders, filesep);
+ opt.derivativesDir = tmp{1};
+ catch
+ % for octave
+ opt.derivativesDir = strjoin(folders, filesep);
+ end
+
+ % Suffix output directory for the saved jobs
+ opt.jobsDir = fullfile(opt.derivativesDir, 'JOBS', opt.taskName);
+
+end
diff --git a/src/setMvpaSuffix.m b/src/setMvpaSuffix.m
deleted file mode 100644
index 7e7d6a93..00000000
--- a/src/setMvpaSuffix.m
+++ /dev/null
@@ -1,6 +0,0 @@
-function mvpaSuffix = setMvpaSuffix(isMVPA)
- mvpaSuffix = '';
- if isMVPA
- mvpaSuffix = '_MVPA';
- end
-end
diff --git a/src/specifyContrasts.m b/src/specifyContrasts.m
deleted file mode 100644
index a6abf910..00000000
--- a/src/specifyContrasts.m
+++ /dev/null
@@ -1,163 +0,0 @@
-function contrasts = specifyContrasts(ffxDir, taskName, opt, isMVPA)
- % Specifies the first level contrasts
- %
- % To know the names of the columns of the design matrix, type :
- % strvcat(SPM.xX.name)
- %
- % EXAMPLE
- % Sn(1) ins 1
- % Sn(1) ins 2
- % Sn(1) T1
- % Sn(1) T2
- % Sn(1) R1
- % Sn(1) R2
- % Sn(1) R3
- % Sn(1) R4
- % Sn(1) R5
- % Sn(1) R6
-
- load(fullfile(ffxDir, 'SPM.mat'));
-
- if isMVPA
- model = spm_jsonread(opt.model.multivariate.file);
- else
- model = spm_jsonread(opt.model.univariate.file);
- end
-
- contrasts = struct('C', [], 'name', []);
- con_counter = 0;
-
- % for the task of interest
- if ~strcmp(model.Input.task, taskName)
- return
- end
-
- % check all the steps specified in the model
- for iStep = 1:length(model.Steps)
-
- Step = model.Steps{iStep};
-
- switch Step.Level
-
- case 'run'
-
- [contrasts, con_counter] = ...
- specifyRunLvlContrasts(contrasts, Step, con_counter, SPM);
-
- case 'subject'
-
- [contrasts, con_counter] = ...
- specifySubLvlContrasts(contrasts, Step, con_counter, SPM);
-
- end
-
- end
-
-end
-
-function [cdt_name, regIdx] = getRegIdx(conList, iCon, SPM, iCdt)
- % get regressors index corresponding to the HRF of of a condition
-
- if iscell(conList)
- cdt_name = conList{iCon};
- elseif isstruct(conList)
- cdt_name = conList(iCon).ConditionList{iCdt};
- end
-
- % get condition name
- cdt_name = strrep(cdt_name, 'trial_type.', '');
-
- % get regressors index corresponding to the HRF of that condition
- regIdx = strfind(SPM.xX.name', [' ' cdt_name '*bf(1)']);
- regIdx = ~cellfun('isempty', regIdx); %#ok<*STRCL1>
-
-end
-
-function [contrasts, con_counter] = specifySubLvlContrasts(contrasts, Step, con_counter, SPM)
-
- if isfield(Step, 'AutoContrasts')
-
- % first the contrasts to compute automatically against baseline
- for iCon = 1:length(Step.AutoContrasts)
-
- con_counter = con_counter + 1;
-
- C = zeros(1, size(SPM.xX.X, 2));
-
- % get regressors index corresponding to the HRF of that condition
- [cdt_name, regIdx] = getRegIdx(Step.AutoContrasts, iCon, SPM);
-
- % give them a value of 1
- C(end, regIdx) = 1;
-
- % stores the specification
- contrasts(con_counter).C = C; %#ok<*AGROW>
- contrasts(con_counter).name = cdt_name;
-
- end
-
- end
-
- if isfield(Step, 'Contrasts')
-
- % then the contrasts that involve contrasting conditions
- % amongst themselves or something inferior to baseline
- for iCon = 1:length(Step.Contrasts)
-
- con_counter = con_counter + 1;
-
- C = zeros(1, size(SPM.xX.X, 2));
-
- for iCdt = 1:length(Step.Contrasts(iCon).ConditionList)
-
- % get regressors index corresponding to the HRF of that condition
- [~, regIdx] = getRegIdx(Step.Contrasts, iCon, SPM, iCdt);
-
- % give them a value of 1
- C(end, regIdx) = Step.Contrasts(iCon).weights(iCdt);
-
- end
-
- % stores the specification
- contrasts(con_counter).C = C;
- contrasts(con_counter).name = ...
- Step.Contrasts(iCon).Name;
-
- end
-
- end
-
-end
-
-function [contrasts, con_counter] = specifyRunLvlContrasts(contrasts, Step, con_counter, SPM)
-
- if isfield(Step, 'AutoContrasts')
-
- % first the contrasts to compute automatically against baseline
- for iCon = 1:length(Step.AutoContrasts)
-
- % get regressors index corresponding to the HRF of that condition
- [cdt_name, regIdx] = getRegIdx(Step.AutoContrasts, iCon, SPM);
-
- regIdx = find(regIdx);
-
- % For each event of each condition, create a seperate
- % contrast
- for iReg = 1:length(regIdx)
-
- C = zeros(1, size(SPM.xX.X, 2));
-
- % add a new line for a new contrast
- con_counter = con_counter + 1;
- C(end, regIdx(iReg)) = 1; % give each event a value of 1
-
- % stores the specification
- contrasts(con_counter).C = C;
- contrasts(con_counter).name = [cdt_name, '_', num2str(iReg)];
-
- end
- end
-
- end
-
-end
diff --git a/src/spmBatching/bidsCopyRawFolder.m b/src/spmBatching/bidsCopyRawFolder.m
deleted file mode 100644
index 1eaf2923..00000000
--- a/src/spmBatching/bidsCopyRawFolder.m
+++ /dev/null
@@ -1,105 +0,0 @@
-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
- % to the derivatives directory.
- % Then it will search the derivatives directory for any zipped nii.gz image
- % and uncompress it to .nii images.
- %
- % INPUT:
- %
- % 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
- %
- % 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
- end
-
- % if input has no opt, load the opt.mat file
- if nargin < 1
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- %% All tasks in this experiment
- % raw directory and derivatives directory
- rawDir = opt.dataDir;
- derivativeDir = fullfile(rawDir, '..', 'derivatives', 'SPM12_CPPL');
-
- % make derivatives folder if it doesnt exist
- if ~exist(derivativeDir, 'dir')
- mkdir(derivativeDir);
- fprintf('derivatives directory created: %s \n', derivativeDir);
- else
- fprintf('derivatives directory already exists. \n');
- end
-
- % copy TSV and JSON file from raw folder if it doesnt exist
- copyfile(fullfile(rawDir, '*.json'), derivativeDir);
- fprintf(' json files copied to derivatives directory \n');
-
- try
- copyfile(fullfile(rawDir, '*.tsv'), derivativeDir);
- fprintf(' tsv files copied to derivatives directory \n');
- catch
- end
-
- %% Loop through the groups, subjects, sessions
-
- group = getData(opt, rawDir);
-
- for iGroup = 1:length(group)
-
- for iSub = 1:group(iGroup).numSub
-
- subID = group(iGroup).subNumber{iSub};
-
- % the folder containing the subjects data
- subDir = ['sub-', subID];
-
- %% copy the whole subject's folder
- % use a call to system cp function to use the derefence option (-L)
- % to get the data 'out' of an eventual datalad dataset
- try
- system( ...
- sprintf('cp -Lr %s %s', ...
- fullfile(rawDir, subDir), ...
- fullfile(derivativeDir, subDir)));
- catch
- message = [ ...
- 'Copying data with system command failed: are you running Windows?\n', ...
- 'Will use matlab/octave copyfile command instead.\n', ...
- 'Could be an issue if your data set contains symbolic links' ...
- '(e.g. if you use datalad or git-annex.'];
- warning(message);
-
- copyfile(fullfile(rawDir, subDir), ...
- fullfile(derivativeDir, subDir));
- end
-
- fprintf('folder copied: %s \n', subDir);
-
- end
- end
-
- %% search for nifti files in a compressed nii.gz format
- zippedNiifiles = spm_select('FPListRec', derivativeDir, '^.*.nii.gz$');
-
- for iFile = 1:size(zippedNiifiles, 1)
-
- file = deblank(zippedNiifiles(iFile, :));
-
- n = load_untouch_nii(file); % load the nifti image
- save_untouch_nii(n, file(1:end - 4)); % Save the functional data as unzipped nii
- fprintf('unzipped: %s \n', file);
-
- if deleteZippedNii == 1
- delete(file); % delete original zipped file
- end
- end
-
-end
diff --git a/src/spmBatching/bidsFFX.m b/src/spmBatching/bidsFFX.m
deleted file mode 100644
index 68b56736..00000000
--- a/src/spmBatching/bidsFFX.m
+++ /dev/null
@@ -1,97 +0,0 @@
-function bidsFFX(action, opt, funcFWHM, isMVPA)
- % bidsFFX(action, funcFWHM, opt, isMVPA)
- %
- % This scripts builds up the design matrix for each subject.
- % It has to be run in 2 separate steps (action):
- %
- % Inputes
- % - action (string)
- % - case specifyAndEstimate = fMRI design and estimate
- % - case contrasts = contrasts
- % - funcFWHM (scalar) is the number of the mm smoothing used in the
- % normalized functional files.
- % for unsmoothed data funcFWHM = 0
- % for smoothed data = ... mm
- % In this way we can make multiple ffx for different smoothing degrees
-
- % if input has no opt, load the opt.mat file
- if nargin < 3
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- if nargin < 4
- isMVPA = 0;
- end
-
- % 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)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- subID = group(iGroup).subNumber{iSub};
-
- mvpaSuffix = setMvpaSuffix(isMVPA);
-
- printProcessingSubject(groupName, iSub, subID);
-
- switch action
-
- case 'specifyAndEstimate'
-
- fprintf(1, 'BUILDING JOB : FMRI design\n');
-
- matlabbatch = setBatchSubjectLevelGLMSpec( ...
- BIDS, opt, subID, funcFWHM, isMVPA);
-
- fprintf(1, 'BUILDING JOB : FMRI estimate\n');
-
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1) = cfg_dep;
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tname = 'Select SPM.mat';
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).name = 'filter';
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(1).value = 'mat';
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).name = 'strtype';
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).tgt_spec{1}(2).value = 'e';
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).sname = ...
- 'fMRI model specification: SPM.mat File';
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_exbranch = ...
- substruct( ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1});
- matlabbatch{2}.spm.stats.fmri_est.spmmat(1).src_output = ...
- substruct('.', 'spmmat');
- matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
- matlabbatch{2}.spm.stats.fmri_est.write_residuals = 1;
-
- saveMatlabBatch(matlabbatch, ...
- ['specifyAndEstimateFfx_task-', opt.taskName, ...
- '_FWHM-', num2str(funcFWHM), ...
- mvpaSuffix], ...
- opt, subID);
-
- case 'contrasts'
-
- fprintf(1, 'BUILDING JOB : FMRI contrasts\n');
-
- matlabbatch = setBatchSubjectLevelContrasts(opt, subID, funcFWHM, isMVPA);
-
- saveMatlabBatch(matlabbatch, ...
- ['contrastsFfx_task-', opt.taskName, ...
- '_FWHM-', num2str(funcFWHM), ...
- mvpaSuffix], ...
- opt, subID);
-
- end
-
- spm_jobman('run', matlabbatch);
-
- end
- end
-
-end
diff --git a/src/spmBatching/bidsRFX.m b/src/spmBatching/bidsRFX.m
deleted file mode 100644
index 072630c0..00000000
--- a/src/spmBatching/bidsRFX.m
+++ /dev/null
@@ -1,395 +0,0 @@
-function bidsRFX(action, funcFWHM, conFWHM, opt, isMVPA)
- % This script smooth all con images created at the fisrt level in each
- % subject, create a mean structural image and mean mask over the
- % population, process the factorial design specification and estimation
- % and estimate Contrats.
- %
- % INPUTS
- % - action
- % - case 'smoothContrasts' : smooth con images
- % - case 'RFX' : Mean Struct, MeanMask, Factorial design specification and
- % estimation, Contrst estimation
- %
- % - funcFWHM = How much smoothing was applied to the functional
- % data in the preprocessing
- % - conFWHM = How much smoothing is required for the CON images for
- % the second level analysis
- %
-
- % if input has no opt, load the opt.mat file
- if nargin < 4
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- if nargin < 5
- isMVPA = 0;
- end
-
- % load the subjects/Groups information and the task name
- [group, opt, ~] = getData(opt);
-
- % Check which level of CON smoothing is desired
- if conFWHM == 0
- smoothPrefix = '';
- elseif conFWHM > 0
- smoothPrefix = ['s', num2str(conFWHM)];
- else
- error ('Check you Con files');
- end
-
- switch action
-
- case 'smoothContrasts'
-
- fprintf(1, 'SMOOTHING CON IMAGES...');
-
- matlabbatch = setBatchSmoothConImages(group, funcFWHM, conFWHM, opt, isMVPA);
-
- saveMatlabBatch( ...
- ['smoothCon_FWHM-', num2str(conFWHM), '_task-', opt.taskName], ...
- 'STC', ...
- opt);
-
- spm_jobman('run', matlabbatch);
-
- case 'RFX'
-
- fprintf(1, 'Create Mean Struct and Mask IMAGES...');
-
- rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
-
- % ------
- % TODO
- % - need to rethink where to save the anat and mask
- % - need to smooth the anat
- % - create a masked version of the anat too
- % ------
-
- matlabbatch = ...
- setBatchMeanAnatAndMask(opt, funcFWHM, isMVPA, rfxDir);
-
- % ------
- % TODO
- % needs to be improved (maybe??) as the structural and mask may vary for
- % different analysis
- % ------
-
- saveMatlabBatch(matlabbatch, 'createMeanStrucMask', opt);
-
- spm_jobman('run', matlabbatch);
-
- %% Factorial design specification
-
- % Load the list of contrasts of interest for the RFX
- % model = spm_jsonread(opt.model.file);
- if isMVPA
- model = spm_jsonread(opt.model.multivariate.file);
- else
- model = spm_jsonread(opt.model.univariate.file);
- end
-
- for iStep = 1:length(model.Steps)
- if strcmp(model.Steps{iStep}.Level, 'dataset')
- grpLvlCon = model.Steps{iStep}.AutoContrasts;
- break
- end
- end
-
- % ------
- % TODO
- % rfxDir should probably be set in setBatchFactorialDesign
- % ------
-
- rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
-
- fprintf(1, 'BUILDING JOB: Factorial Design Specification');
-
- matlabbatch = setBatchFactorialDesign(grpLvlCon, group, smoothPrefix, rfxDir);
-
- % ------
- % TODO
- % needs to be improved (maybe??) as the name may vary with FXHM and
- % contrast
- % ------
-
- saveMatlabBatch(matlabbatch, 'rfxSpecification', opt);
-
- fprintf(1, 'Factorial Design Specification...');
-
- spm_jobman('run', matlabbatch);
-
- %% Factorial design estimation
-
- fprintf(1, 'BUILDING JOB: Factorial Design Estimation');
-
- matlabbatch = {};
-
- for j = 1:size(grpLvlCon, 1)
- conName = rmTrialTypeStr(grpLvlCon{j});
- matlabbatch{j}.spm.stats.fmri_est.spmmat = ...
- { fullfile(rfxDir, conName, 'SPM.mat') };
- matlabbatch{j}.spm.stats.fmri_est.method.Classical = 1;
- end
-
- % ------
- % TODO
- % needs to be improved (maybe??) as the name may vary with FXHM and
- % contrast
- % ------
-
- saveMatlabBatch(matlabbatch, 'rfxEstimation', opt);
-
- fprintf(1, 'Factorial Design Estimation...');
-
- spm_jobman('run', matlabbatch);
-
- %% Contrast estimation
-
- fprintf(1, 'BUILDING JOB: Contrast estimation');
-
- matlabbatch = {};
-
- % ADD/REMOVE CONTRASTS DEPENDING ON YOUR EXPERIMENT AND YOUR GROUPS
- for j = 1:size(grpLvlCon, 1)
- conName = rmTrialTypeStr(grpLvlCon{j});
- matlabbatch{j}.spm.stats.con.spmmat = ...
- {fullfile(rfxDir, conName, 'SPM.mat')};
- matlabbatch{j}.spm.stats.con.consess{1}.tcon.name = 'GROUP';
- matlabbatch{j}.spm.stats.con.consess{1}.tcon.convec = 1;
- matlabbatch{j}.spm.stats.con.consess{1}.tcon.sessrep = 'none';
-
- matlabbatch{j}.spm.stats.con.delete = 0;
- end
-
- % ------
- % TODO
- % needs to be improved (maybe??) as the name may vary with FXHM and
- % contrast
- % ------
-
- saveMatlabBatch(matlabbatch, 'rfxContrasts', opt);
-
- fprintf(1, 'Contrast Estimation...');
-
- spm_jobman('run', matlabbatch);
-
- end
-
-end
-
-function conName = rmTrialTypeStr(conName)
- conName = strrep(conName, 'trial_type.', '');
-end
-
-function matlabbatch = setBatchMeanAnatAndMask(opt, funcFWHM, isMVPA, rfxDir)
-
- [group, opt, BIDS] = getData(opt);
-
- matlabbatch = {};
-
- % Create Mean Structural Image
- fprintf(1, 'BUILDING JOB: Create Mean Structural Image...');
-
- subCounter = 0;
-
- for iGroup = 1:length(group)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- subCounter = subCounter + 1;
-
- subID = group(iGroup).subNumber{iSub};
-
- printProcessingSubject(groupName, iSub, subID);
-
- %% STRUCTURAL
- struct = spm_BIDS(BIDS, 'data', ...
- 'sub', subID, ...
- 'type', 'T1w');
- % we assume that the first T1w is the correct one (could be an
- % issue for data set with more than one
- struct = struct{1};
-
- [subStrucDataDir, file, ext] = spm_fileparts(struct);
-
- % get filename of the orginal file (drop the gunzip extension)
- if strcmp(ext, '.gz')
- fileName = file;
- elseif strcmp(ext, '.nii')
- fileName = [file ext];
- end
-
- files = inputFileValidation( ...
- subStrucDataDir, ...
- [spm_get_defaults('normalise.write.prefix'), ...
- spm_get_defaults('deformations.modulate.prefix')], ...
- fileName);
-
- matlabbatch{1}.spm.util.imcalc.input{subCounter, :} = files{1};
-
- %% Mask
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
-
- files = inputFileValidation(ffxDir, '', 'mask.nii');
-
- matlabbatch{2}.spm.util.imcalc.input{subCounter, :} = files{1};
-
- end
- end
-
- %% Generate the equation to get the mean of the mask and structural image
- % example : if we have 5 subjects, Average equation = '(i1+i2+i3+i4+i5)/5'
- nbImg = subCounter;
- imgRange = 1:subCounter;
-
- tmpImg = sprintf('+i%i', imgRange);
- tmpImg = tmpImg(2:end);
- sumEquation = ['(', tmpImg, ')'];
-
- % meanStruct_equation = '(i1+i2+i3+i4+i5)/5'
- meanStruct_equation = ['(', tmpImg, ')/', num2str(nbImg)];
-
- % ------
- % TODO
- % not sure this makes sense for the mask as voxels that have no data for one
- % subject are excluded anyway !!!!
-
- % meanMask_equation = '(i1+i2+i3+i4+i5)>0.75*5'
- meanMask_equation = strcat(sumEquation, '>0.75*', num2str(nbImg));
-
- %% The mean structural will be saved in the RFX folder
- matlabbatch{1}.spm.util.imcalc.output = 'meanAnat.nii';
- matlabbatch{1}.spm.util.imcalc.outdir{:} = rfxDir;
- matlabbatch{1}.spm.util.imcalc.expression = meanStruct_equation;
- 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;
-
- %% The mean mask will be saved in the RFX folder
- matlabbatch{2}.spm.util.imcalc.output = 'meanMask.nii';
- matlabbatch{2}.spm.util.imcalc.outdir{:} = rfxDir;
- matlabbatch{2}.spm.util.imcalc.expression = meanMask_equation;
- matlabbatch{2}.spm.util.imcalc.options.dmtx = 0;
- matlabbatch{2}.spm.util.imcalc.options.mask = 0;
- matlabbatch{2}.spm.util.imcalc.options.interp = 1;
- matlabbatch{2}.spm.util.imcalc.options.dtype = 4;
-
-end
-
-function matlabbatch = setBatchSmoothConImages(group, funcFWHM, conFWHM, opt, isMVPA)
-
- counter = 0;
-
- matlabbatch = {};
-
- %% Loop through the groups, subjects, and sessions
- for iGroup = 1:length(group)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- counter = counter + 1;
-
- subNumber = group(iGroup).subNumber{iSub};
-
- printProcessingSubject(groupName, iSub, subNumber);
-
- ffxDir = getFFXdir(subNumber, funcFWHM, opt, isMVPA);
-
- conImg = spm_select('FPlist', ffxDir, '^con*.*nii$');
- matlabbatch{counter}.spm.spatial.smooth.data = cellstr(conImg);
-
- % Define how much smoothing is required
- matlabbatch{counter}.spm.spatial.smooth.fwhm = ...
- [conFWHM conFWHM conFWHM];
- matlabbatch{counter}.spm.spatial.smooth.dtype = 0;
- matlabbatch{counter}.spm.spatial.smooth.prefix = [ ...
- spm_get_defaults('smooth.prefix'), num2str(conFWHM)];
-
- end
- end
-
-end
-
-function matlabbatch = setBatchFactorialDesign(grpLvlCon, group, smoothPrefix, rfxDir)
-
- con = 0;
-
- % For each contrast
- for j = 1:size(grpLvlCon, 1)
-
- % the strrep(Session{j}, 'trial_type.', '') is there to remove
- % 'trial_type.' because contrasts against baseline are renamed
- % at the subject level
- conName = rmTrialTypeStr(grpLvlCon{j});
-
- con = con + 1;
-
- % For each group
- for iGroup = 1:length(group)
-
- groupName = group(iGroup).name;
-
- matlabbatch{j}.spm.stats.factorial_design.des.fd.icell(iGroup).levels = ...
- iGroup; %#ok<*AGROW>
-
- for iSub = 1:group(iGroup).numSub
-
- subID = group(iGroup).subNumber{iSub};
-
- printProcessingSubject(groupName, iSub, subID);
-
- % FFX directory and load SPM.mat of that subject
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
- load(fullfile(ffxDir, 'SPM.mat'));
-
- % find which contrast of that subject has the name of the contrast we
- % want to bring to the group level
- conIdx = find(strcmp({SPM.xCon.name}, conName));
- fileName = sprintf('con_%0.4d.nii', conIdx);
- file = inputFileValidation(ffxDir, smoothPrefix, fileName);
-
- matlabbatch{j}.spm.stats.factorial_design.des.fd.icell(iGroup).scans(iSub, :) = ...
- file;
-
- end
-
- end
-
- % GROUP and the number of levels in the group. if 2 groups ,
- % then number of levels = 2
- matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.name = 'GROUP';
- matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.levels = 1;
- matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.dept = 0;
-
- % 1: Assumes that the variance is not the same across groups
- % 0: There is no difference in the variance between groups
- matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.variance = 1;
- matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.gmsca = 0;
- matlabbatch{j}.spm.stats.factorial_design.des.fd.fact.ancova = 0;
- % matlabbatch{j}.spm.stats.factorial_design.cov = [];
- matlabbatch{j}.spm.stats.factorial_design.masking.tm.tm_none = 1;
- matlabbatch{j}.spm.stats.factorial_design.masking.im = 1;
- matlabbatch{j}.spm.stats.factorial_design.masking.em = { ...
- fullfile(rfxDir, 'MeanMask.nii')};
- matlabbatch{j}.spm.stats.factorial_design.globalc.g_omit = 1;
- matlabbatch{j}.spm.stats.factorial_design.globalm.gmsca.gmsca_no = 1;
- matlabbatch{j}.spm.stats.factorial_design.globalm.glonorm = 1;
-
- % If it exists, issue a warning that it has been overwritten
- if exist(fullfile(rfxDir, conName), 'dir')
- warning('overwriting directory: %s \n', fullfile(rfxDir, conName));
- rmdir(fullfile(rfxDir, conName), 's');
- end
-
- mkdir(fullfile(rfxDir, conName));
- matlabbatch{j}.spm.stats.factorial_design.dir = { fullfile(rfxDir, conName) };
-
- end
-
-end
diff --git a/src/spmBatching/bidsRealignReslice.m b/src/spmBatching/bidsRealignReslice.m
deleted file mode 100644
index 35a5a671..00000000
--- a/src/spmBatching/bidsRealignReslice.m
+++ /dev/null
@@ -1,44 +0,0 @@
-function bidsRealignReslice(opt)
- % bidsRealignReslice(opt)
- %
- % The scripts realigns the functional
- % Assumes that bidsSTC has already been run
-
- %% TO DO
- % find a way to paralelize this over subjects
-
- % if input has no opt, load the opt.mat file
- if nargin < 1
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- % 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)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- % Get the ID of the subject
- % (i.e SubNumber doesnt have to match the iSub if one subject
- % is exluded for any reason)
- subID = group(iGroup).subNumber{iSub}; % Get the subject ID
-
- printProcessingSubject(groupName, iSub, subID);
-
- fprintf(1, ' BUILDING SPATIAL JOB : REALIGN\n');
-
- matlabbatch = setBatchRealignReslice(BIDS, opt, subID);
-
- saveMatlabBatch(matlabbatch, 'RealignReslice', opt, subID);
-
- spm_jobman('run', matlabbatch);
-
- end
- end
-
-end
diff --git a/src/spmBatching/bidsResults.m b/src/spmBatching/bidsResults.m
deleted file mode 100644
index ed43cd74..00000000
--- a/src/spmBatching/bidsResults.m
+++ /dev/null
@@ -1,169 +0,0 @@
-function bidsResults(opt, funcFWHM, conFWHM, isMVPA)
- % This scripts computes the results for a series of contrast that can be
- % specified at the run, subject or dataset step level (see contrast specification
- % following the BIDS stats model specification)
- %
- % funcFWHM is the number of the mm smoothing used on the normalized functional files.
- % for unsmoothied data FFXSmoothing = 0
-
- % if input has no opt, load the opt.mat file
- if nargin < 1
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- % load the subjects/Groups information and the task name
- [group, opt] = getData(opt);
-
- matlabbatch = [];
-
- % loop trough the steps and more results to compute for each contrast
- % mentioned for each step
- for iStep = 1:length(opt.result.Steps)
-
- for iCon = 1:length(opt.result.Steps(iStep).Contrasts)
-
- % Depending on the level step we migh have to define a matlabbatch
- % for each subject or just on for the whole group
- switch opt.result.Steps(iStep).Level
-
- case 'run'
- warning('run level not implemented yet');
-
- % saveMatlabBatch(matlabbatch, 'computeFfxResults', opt, subID);
-
- case 'subject'
-
- matlabbatch = ...
- setBatchSubjectLevelResults( ...
- matlabbatch, ...
- group, ...
- funcFWHM, ...
- opt, ...
- isMVPA, ...
- iStep, ...
- iCon);
-
- % TODO
- % Save this batch in for each subject and not once for all
-
- saveMatlabBatch(matlabbatch, 'computeFfxResults', opt);
-
- case 'dataset'
-
- rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
-
- load(fullfile(rfxDir, 'SPM.mat'));
-
- results.dir = rfxDir;
- results.contrastNb = 1;
- results.label = 'group level';
- results.nbSubj = SPM.nscan;
-
- matlabbatch = resultsMatlabbatch( ...
- matlabbatch, opt, iStep, iCon, results);
-
- saveMatlabBatch(matlabbatch, 'computeRfxResults', opt);
-
- end
- end
-
- end
-
- if ~isempty(matlabbatch)
-
- spm_jobman('run', matlabbatch);
-
- % move ps file
- % TODO
-
- % rename NIDM file
- % TODO
- end
-
-end
-
-function batch = resultsMatlabbatch(batch, opt, iStep, iCon, results)
- % outputs the typical matlabbatch to compute the results for a given
- % contrast
-
- batch{end + 1}.spm.stats.results.spmmat = {fullfile(results.dir, 'SPM.mat')};
-
- batch{end}.spm.stats.results.conspec.titlestr = ...
- opt.result.Steps(iStep).Contrasts(iCon).Name;
-
- batch{end}.spm.stats.results.conspec.contrasts = results.contrastNb;
-
- batch{end}.spm.stats.results.conspec.threshdesc = ...
- opt.result.Steps(iStep).Contrasts(iCon).MC;
-
- batch{end}.spm.stats.results.conspec.thresh = opt.result.Steps(iStep).Contrasts(iCon).p;
-
- batch{end}.spm.stats.results.conspec.extent = opt.result.Steps(iStep).Contrasts(iCon).k;
-
- batch{end}.spm.stats.results.conspec.conjunction = 1;
-
- batch{end}.spm.stats.results.conspec.mask.none = ...
- ~opt.result.Steps(iStep).Contrasts(iCon).Mask;
-
- batch{end}.spm.stats.results.units = 1;
-
- batch{end}.spm.stats.results.export{1}.ps = true;
-
- if opt.result.Steps(1).Contrasts(iCon).NIDM
-
- batch{end}.spm.stats.results.export{2}.nidm.modality = 'FMRI';
-
- batch{end}.spm.stats.results.export{2}.nidm.refspace = 'ixi';
-
- batch{end}.spm.stats.results.export{2}.nidm.group.nsubj = results.nbSubj;
-
- batch{end}.spm.stats.results.export{2}.nidm.group.label = results.label;
-
- end
-
-end
-
-function batch = setBatchSubjectLevelResults(varargin)
-
- [batch, grp, funcFWHM, opt, isMVPA, iStep, iCon] = deal(varargin{:});
-
- for iGroup = 1:length(grp)
-
- % For each subject
- for iSub = 1:grp(iGroup).numSub
-
- % Get the Subject ID
- subID = grp(iGroup).subNumber{iSub};
-
- % FFX Directory
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
-
- load(fullfile(ffxDir, 'SPM.mat'));
-
- % identify which contrast nb actually has the name the user asked
- conNb = find( ...
- strcmp({SPM.xCon.name}', ...
- opt.result.Steps(iStep).Contrasts(iCon).Name));
-
- if isempty(conNb)
- sprintf('List of contrast in this SPM file');
- disp({SPM.xCon.name}');
- error( ...
- 'This SPM file %s does not contain a contrast named %s', ...
- fullfile(Dir, 'SPM.mat'), ...
- opt.result.Steps(1).Contrasts(iCon).Name);
- end
-
- results.dir = ffxDir;
- results.contrastNb = conNb;
- results.label = subID;
- results.nbSubj = 1;
-
- batch = resultsMatlabbatch( ...
- batch, opt, iStep, iCon, results);
-
- end
- end
-
-end
diff --git a/src/spmBatching/bidsSTC.m b/src/spmBatching/bidsSTC.m
deleted file mode 100644
index 63db7a58..00000000
--- a/src/spmBatching/bidsSTC.m
+++ /dev/null
@@ -1,67 +0,0 @@
-function bidsSTC(opt)
- % Performs SLICE TIMING CORRECTION of the functional data. The
- % script for each subject and can handle multiple sessions and multiple
- % runs.
- %
- % Slice timing units is in milliseconds to be BIDS compliant and not in slice number
- % as is more traditionally the case with SPM.
- %
- % In the case the slice timing information was not specified in the json FILES
- % in the BIDS data set (e.g it couldnt be extracted from the trento old scanner),
- % then add this information manually in opt.sliceOrder field.
- %
- % If this is empty the slice timing correction will not be performed
- %
- % If not specified this function will take the mid-volume time point as reference
- % to do the slice timing correction
- %
- % See README.md for more information about slice timing correction
- %
- % INPUT:
- % 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
-
- % if input has no opt, load the opt.mat file
- if nargin < 1
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- % load the subjects/Groups information and the task name
- [group, opt, BIDS] = getData(opt);
-
- fprintf(1, 'DOING SLICE TIME CORRECTION\n');
-
- %% Loop through the groups, subjects, and sessions
- for iGroup = 1:length(group)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- % Get the ID of the subject
- % (i.e SubNumber doesnt have to match the iSub if one subject is exluded)
- subID = group(iGroup).subNumber{iSub};
-
- printProcessingSubject(groupName, iSub, subID);
-
- %% GET FUNCTIOVAL FILES
- fprintf(1, ' BUILDING STC JOB : STC\n');
-
- matlabbatch = setBatchSTC(BIDS, opt, subID);
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{1}.spm.temporal.st.prefix = spm_get_defaults('slicetiming.prefix');
-
- if ~isempty(matlabbatch)
- saveMatlabBatch(matlabbatch, 'STC', opt, subID);
-
- spm_jobman('run', matlabbatch);
- end
-
- end
- end
-
-end
diff --git a/src/spmBatching/bidsSmoothing.m b/src/spmBatching/bidsSmoothing.m
deleted file mode 100644
index c6a34665..00000000
--- a/src/spmBatching/bidsSmoothing.m
+++ /dev/null
@@ -1,36 +0,0 @@
-function bidsSmoothing(funcFWHM, opt)
- % This scripts performs smoothing to the functional data using a full width
- % half maximum smoothing kernel of size "mm_smoothing".
-
- % if input has no opt, load the opt.mat file
- if nargin < 2
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- % 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)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- subID = group(iGroup).subNumber{iSub};
-
- printProcessingSubject(groupName, iSub, subID);
-
- fprintf(1, 'PREPARING: SMOOTHING JOB \n');
-
- matlabbatch = setBatchSmoothing(BIDS, opt, subID, funcFWHM);
-
- saveMatlabBatch(matlabbatch, 'Smoothing', opt, subID);
-
- spm_jobman('run', matlabbatch);
-
- end
- end
-
-end
diff --git a/src/spmBatching/bidsSpatialPrepro.m b/src/spmBatching/bidsSpatialPrepro.m
deleted file mode 100644
index edc1ee18..00000000
--- a/src/spmBatching/bidsSpatialPrepro.m
+++ /dev/null
@@ -1,372 +0,0 @@
-function bidsSpatialPrepro(opt)
- %% The scripts performs spatial preprocessing of the functional and structural data.
- % The structural data are segmented and normalized to MNI space.
- % The functional data are re-aligned, coregistered with the structural and
- % normalized to MNI space.
-
- %% TO DO
- % find a way to paralelize this over subjects
-
- % if input has no opt, load the opt.mat file
- if nargin < 1
- load('opt.mat');
- fprintf('opt.mat file loaded \n\n');
- end
-
- % load the subjects/Groups information and the task name
- [group, opt, BIDS] = getData(opt);
-
- % Indicate which session the structural data was collected
- structSession = 1;
-
- % creates prefix to look for
- prefix = getPrefix('preprocess', opt);
-
- fprintf(1, 'DOING PREPROCESSING\n');
-
- %% Loop through the groups, subjects, and sessions
- for iGroup = 1:length(group)
-
- groupName = group(iGroup).name;
-
- for iSub = 1:group(iGroup).numSub
-
- matlabbatch = [];
- % Get the ID of the subject
- % (i.e SubNumber doesnt have to match the iSub if one subject
- % is exluded for any reason)
- subID = group(iGroup).subNumber{iSub}; % Get the subject ID
-
- printProcessingSubject(groupName, iSub, subID);
-
- % identify sessions for this subject
- [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
-
- % get all runs for that subject across all sessions
- anat = spm_BIDS(BIDS, 'data', ...
- 'sub', subID, ...
- 'ses', sessions{structSession}, ...
- 'type', 'T1w');
- % we assume that the first T1w is the correct one (could be an
- % issue for dataset with more than one
- anat = anat{1};
-
- %% Structural file directory
- [subStrucDataDir, structFile, ext] = spm_fileparts(anat);
-
- if strcmp(ext, '.gz')
- % unzip nii.gz structural file to be read by SPM
- anat = load_untouch_nii(anat);
- save_untouch_nii(anat, fullfile(subStrucDataDir, structFile));
- [anatImage] = fullfile(subStrucDataDir, structFile);
- else
- [anatImage] = fullfile(subStrucDataDir, [structFile ext]);
- end
-
- % NAMED FILE SELECTOR
- matlabbatch{1}.cfg_basicio.cfg_named_file.name = 'Anatomical';
- matlabbatch{1}.cfg_basicio.cfg_named_file.files = { {anatImage} };
-
- %% REALIGN
- fprintf(1, ' BUILDING SPATIAL JOB : REALIGN\n');
- sesCounter = 1;
-
- for iSes = 1:nbSessions % For each session
-
- % get all runs for that subject across all sessions
- [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
-
- for iRun = 1:nbRuns % For each run
-
- % get the filename for this bold run for this task
- [fileName, subFuncDataDir] = getBoldFilename( ...
- BIDS, ...
- subID, sessions{iSes}, runs{iRun}, opt);
-
- % check that the file with the right prefix exist
- files = inputFileValidation(subFuncDataDir, prefix, fileName);
-
- [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName);
-
- fprintf(1, ' %s\n', files{1});
-
- matlabbatch{2}.spm.spatial.realign.estwrite.data{sesCounter} = ...
- cellstr(files);
-
- sesCounter = sesCounter + 1;
-
- end
- end
-
- matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.weight = {''};
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.quality = 1;
- % matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.sep = 2;
- % matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.fwhm = 5;
- % matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.rtm = 1;
- % matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.interp = 2;
- % matlabbatch{2}.spm.spatial.realign.estwrite.eoptions.wrap = [0 0 0];
- % matlabbatch{2}.spm.spatial.realign.estwrite.roptions.which = [0 1];
- % matlabbatch{2}.spm.spatial.realign.estwrite.roptions.interp = 3;
- % matlabbatch{2}.spm.spatial.realign.estwrite.roptions.wrap = [0 0 0];
- % matlabbatch{2}.spm.spatial.realign.estwrite.roptions.mask = 1;
-
- %% COREGISTER
- % REFERENCE IMAGE : DEPENDENCY FROM NAMED FILE SELECTOR ('Structural')
- fprintf(1, ' BUILDING SPATIAL JOB : COREGISTER\n');
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1) = cfg_dep;
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).tname = 'Reference Image';
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).tgt_spec{1}(1).name = 'class';
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).tgt_spec{1}(1).value = 'cfg_files';
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).tgt_spec{1}(2).name = 'strtype';
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).tgt_spec{1}(2).value = 'e';
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).sname = ...
- 'Named File Selector: Structural(1) - Files';
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).src_exbranch = ...
- substruct( ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1});
- matlabbatch{3}.spm.spatial.coreg.estimate.ref(1).src_output = ...
- substruct('.', 'files', '{}', {1});
-
- % SOURCE IMAGE : DEPENDENCY FROM REALIGNEMENT
- % ('Realign: Estimate & Reslice: Mean Image')
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1) = cfg_dep;
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).tname = 'Source Image';
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).tgt_spec{1}(1).name = 'filter';
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).tgt_spec{1}(1).value = 'image';
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).tgt_spec{1}(2).name = 'strtype';
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).tgt_spec{1}(2).value = 'e';
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).sname = ...
- 'Realign: Estimate & Reslice: Mean Image';
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).src_exbranch = ...
- substruct( ...
- '.', 'val', '{}', {2}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1});
- matlabbatch{3}.spm.spatial.coreg.estimate.source(1).src_output = ...
- substruct('.', 'rmean');
-
- % OTHER IMAGES : DEPENDENCY FROM REALIGNEMENT ('Realign: Estimate & Reslice:
- % Realigned Images (Sess 1 to N)')
- % files %%
- for iSes = 1:sesCounter - 1 % '-1' because I added 1 extra session to ses_counter
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes) = cfg_dep;
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).tname = 'Other Images';
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).tgt_spec{1}(1).name = ...
- 'filter';
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).tgt_spec{1}(1).value = ...
- 'image';
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).tgt_spec{1}(2).name = ...
- 'strtype';
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).tgt_spec{1}(2).value = ...
- 'e';
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).sname = ...
- ['Realign: Estimate & Reslice: Realigned Images (Sess ' (iSes) ')'];
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).src_exbranch = ...
- substruct( ...
- '.', 'val', '{}', {2}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1});
- matlabbatch{3}.spm.spatial.coreg.estimate.other(iSes).src_output = ...
- substruct('.', 'sess', '()', {iSes}, '.', 'cfiles');
- end
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{3}.spm.spatial.coreg.estimate.eoptions.cost_fun = 'nmi';
- % matlabbatch{3}.spm.spatial.coreg.estimate.eoptions.sep = [4 2];
- % matlabbatch{3}.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{3}.spm.spatial.coreg.estimate.eoptions.fwhm = [7 7];
-
- %% SEGMENT STRUCTURALS
- % (WITH NEW SEGMENT -DEFAULT SEGMENT IN SPM12)
- % DATA : DEPENDENCY FROM NAMED FILE SELECTOR ('Anatomical')
- fprintf(1, ' BUILDING SPATIAL JOB : SEGMENT STRUCTURAL\n');
-
- matlabbatch = setBatchSegmentation(matlabbatch);
-
- %% NORMALIZE FUNCTIONALS
- fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE FUNCTIONALS\n');
-
- for iJob = 5:9
- matlabbatch{iJob}.spm.spatial.normalise.write.subj.def(1) = ...
- cfg_dep('Segment: Forward Deformations', ...
- substruct( ...
- '.', 'val', '{}', {4}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct('.', 'fordef', '()', {':'}));
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{iJob}.spm.spatial.normalise.write.woptions.bb = ...
- % [-78 -112 -70 ; 78 76 85];
- % matlabbatch{iJob}.spm.spatial.normalise.write.woptions.interp = 4;
- % matlabbatch{iJob}.spm.spatial.normalise.write.woptions.prefix = ...
- % spm_get_defaults('normalise.write.prefix');
-
- end
-
- matlabbatch{5}.spm.spatial.normalise.write.subj.resample(1) = ...
- cfg_dep('Coregister: Estimate: Coregistered Images', ...
- substruct( ...
- '.', 'val', '{}', {3}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct('.', 'cfiles'));
-
- % original voxel size at acquisition
- matlabbatch{5}.spm.spatial.normalise.write.woptions.vox = voxDim;
-
- % NORMALIZE STRUCTURAL
- fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE STRUCTURAL\n');
- matlabbatch{6}.spm.spatial.normalise.write.subj.resample(1) = ...
- cfg_dep('Segment: Bias Corrected (1)', ...
- substruct( ...
- '.', 'val', '{}', {4}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct( ...
- '.', 'channel', '()', {1}, ...
- '.', 'biascorr', '()', {':'}));
- % size 3 allow to run RunQA / original voxel size at acquisition
- matlabbatch{6}.spm.spatial.normalise.write.woptions.vox = [1 1 1];
-
- % NORMALIZE GREY MATTER
- fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE GREY MATTER\n');
- matlabbatch{7}.spm.spatial.normalise.write.subj.resample(1) = ...
- cfg_dep('Segment: c1 Images', ...
- substruct( ...
- '.', 'val', '{}', {4}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct( ...
- '.', 'tiss', '()', {1}, ...
- '.', 'c', '()', {':'}));
- % size 3 allow to run RunQA / original voxel size at acquisition
- matlabbatch{7}.spm.spatial.normalise.write.woptions.vox = voxDim;
-
- % NORMALIZE WHITE MATTER
- fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE WHITE MATTER\n');
- matlabbatch{8}.spm.spatial.normalise.write.subj.resample(1) = ...
- cfg_dep('Segment: c2 Images', ...
- substruct( ...
- '.', 'val', '{}', {4}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct( ...
- '.', 'tiss', '()', {2}, ...
- '.', 'c', '()', {':'}));
- % size 3 allow to run RunQA / original voxel size at acquisition
- matlabbatch{8}.spm.spatial.normalise.write.woptions.vox = voxDim;
-
- % NORMALIZE CSF MATTER
- fprintf(1, ' BUILDING SPATIAL JOB : NORMALIZE CSF\n');
- matlabbatch{9}.spm.spatial.normalise.write.subj.resample(1) = ...
- cfg_dep('Segment: c3 Images', ...
- substruct( ...
- '.', 'val', '{}', {4}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct( ...
- '.', 'tiss', '()', {3}, ...
- '.', 'c', '()', {':'}));
- % size 3 allow to run RunQA / original voxel size at acquisition
- matlabbatch{9}.spm.spatial.normalise.write.woptions.vox = voxDim;
-
- saveMatlabBatch(matlabbatch, 'spatialPreprocessing', opt, subID);
-
- spm_jobman('run', matlabbatch);
-
- end
- end
-
-end
-
-function [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName)
-
- % get native resolution to reuse it at normalisation;
- if ~isempty(opt.funcVoxelDims) % If voxel dimensions is defined in the opt
- voxDim = opt.funcVoxelDims; % Get the dimension values
- else
- % SPM Doesnt deal with nii.gz and all our nii will be unzipped
- % at this stage
- hdr = spm_vol(fullfile(subFuncDataDir, [prefix, fileName]));
- voxDim = diag(hdr(1).mat);
- % Voxel dimensions are not pure integers before reslicing, therefore
- % Round the dimensions of the functional files to the 1st decimal point
- voxDim = abs(voxDim(1:3)');
- voxDim = round(voxDim * 10) / 10;
- % Add it to opt.funcVoxelDims to have the same value for
- % all subjects and sessions
- opt.funcVoxelDims = voxDim;
- end
-end
-
-function matlabbatch = setBatchSegmentation(matlabbatch)
-
- % define SPM folder
- spmLocation = spm('dir');
-
- % SAVE BIAS CORRECTED IMAGE
- matlabbatch{4}.spm.spatial.preproc.channel.vols(1) = ...
- cfg_dep('Named File Selector: Structural(1) - Files', ...
- substruct( ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}, ...
- '.', 'val', '{}', {1}), ...
- substruct('.', 'files', '{}', {1}));
- matlabbatch{4}.spm.spatial.preproc.channel.biasreg = 0.001;
- matlabbatch{4}.spm.spatial.preproc.channel.biasfwhm = 60;
- matlabbatch{4}.spm.spatial.preproc.channel.write = [0 1];
-
- % CREATE SEGMENTS IN NATIVE SPACE OF GM,WM AND CSF
- % (CSF - in case I want to compute TIV later - stefbenet)
- matlabbatch{4}.spm.spatial.preproc.tissue(1).tpm = ...
- {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',1']};
- matlabbatch{4}.spm.spatial.preproc.tissue(1).ngaus = 1;
- matlabbatch{4}.spm.spatial.preproc.tissue(1).native = [1 1];
- matlabbatch{4}.spm.spatial.preproc.tissue(1).warped = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(2).tpm = ...
- {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',2']};
- matlabbatch{4}.spm.spatial.preproc.tissue(2).ngaus = 1;
- matlabbatch{4}.spm.spatial.preproc.tissue(2).native = [1 1];
- matlabbatch{4}.spm.spatial.preproc.tissue(2).warped = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(3).tpm = ...
- {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',3']};
- matlabbatch{4}.spm.spatial.preproc.tissue(3).ngaus = 2;
- matlabbatch{4}.spm.spatial.preproc.tissue(3).native = [1 1];
- matlabbatch{4}.spm.spatial.preproc.tissue(3).warped = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(4).tpm = ...
- {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',4']};
- matlabbatch{4}.spm.spatial.preproc.tissue(4).ngaus = 3;
- matlabbatch{4}.spm.spatial.preproc.tissue(4).native = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(4).warped = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(5).tpm = ...
- {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',5']};
- matlabbatch{4}.spm.spatial.preproc.tissue(5).ngaus = 4;
- matlabbatch{4}.spm.spatial.preproc.tissue(5).native = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(5).warped = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(6).tpm = ...
- {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',6']};
- matlabbatch{4}.spm.spatial.preproc.tissue(6).ngaus = 2;
- matlabbatch{4}.spm.spatial.preproc.tissue(6).native = [0 0];
- matlabbatch{4}.spm.spatial.preproc.tissue(6).warped = [0 0];
-
- % SAVE FORWARD DEFORMATION FIELD FOR NORMALISATION
- matlabbatch{4}.spm.spatial.preproc.warp.mrf = 1;
- matlabbatch{4}.spm.spatial.preproc.warp.cleanup = 1;
- matlabbatch{4}.spm.spatial.preproc.warp.reg = [0 0.001 0.5 0.05 0.2];
- matlabbatch{4}.spm.spatial.preproc.warp.affreg = 'mni';
- matlabbatch{4}.spm.spatial.preproc.warp.fwhm = 0;
- matlabbatch{4}.spm.spatial.preproc.warp.samp = 3;
- matlabbatch{4}.spm.spatial.preproc.warp.write = [1 1];
-end
diff --git a/src/spmBatching/setBatchRealignReslice.m b/src/spmBatching/setBatchRealignReslice.m
deleted file mode 100644
index b5069353..00000000
--- a/src/spmBatching/setBatchRealignReslice.m
+++ /dev/null
@@ -1,52 +0,0 @@
-function matlabbatch = setBatchRealignReslice(BIDS, opt, subID)
-
- % identify sessions for this subject
- [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
-
- prefix = getPrefix('preprocess', opt);
-
- matlabbatch = [];
-
- sesCounter = 1;
-
- for iSes = 1:nbSessions % For each session
-
- % get all runs for that subject across all sessions
- [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
-
- for iRun = 1:nbRuns % For each run
-
- % get the filename for this bold run for this task
- [fileName, subFuncDataDir] = getBoldFilename( ...
- BIDS, ...
- subID, sessions{iSes}, runs{iRun}, opt);
-
- % check that the file with the right prefix exist
- files = inputFileValidation(subFuncDataDir, prefix, fileName);
-
- fprintf(1, ' %s\n', files{1});
-
- matlabbatch{1}.spm.spatial.realign.estwrite.data{sesCounter} = ...
- cellstr(files);
-
- sesCounter = sesCounter + 1;
-
- end
- end
-
- matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.weight = {''};
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.quality = 1;
- % matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.sep = 2;
- % matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.fwhm = 5;
- % matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.rtm = 1;
- % matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.interp = 2;
- % matlabbatch{1}.spm.spatial.realign.estwrite.eoptions.wrap = [0 0 0];
- % matlabbatch{1}.spm.spatial.realign.estwrite.roptions.which = [0 1];
- % matlabbatch{1}.spm.spatial.realign.estwrite.roptions.interp = 3;
- % matlabbatch{1}.spm.spatial.realign.estwrite.roptions.wrap = [0 0 0];
- % matlabbatch{1}.spm.spatial.realign.estwrite.roptions.mask = 1;
-
-end
diff --git a/src/spmBatching/setBatchSTC.m b/src/spmBatching/setBatchSTC.m
deleted file mode 100644
index 96dc3660..00000000
--- a/src/spmBatching/setBatchSTC.m
+++ /dev/null
@@ -1,85 +0,0 @@
-function matlabbatch = setBatchSTC(BIDS, opt, subID)
- % Slice timing units is in milliseconds to be BIDS compliant and not in slice number
- % as is more traditionally the case with SPM.
- %
- % In the case the slice timing information was not specified in the json FILES
- % in the BIDS data set (e.g it couldnt be extracted from the trento old scanner),
- % then add this information manually in opt.sliceOrder field.
- %
- % If this is empty the slice timing correction will not be performed
- %
- % If not specified this function will take the mid-volume time point as reference
- % to do the slice timing correction
- %
- % See README.md for more information about slice timing correction
-
- matlabbatch = [];
-
- % get slice order
- sliceOrder = getSliceOrder(opt, 1);
- if isempty(sliceOrder)
- warning('No slice order dectected: skipping slice timing correction.');
- return
- end
-
- % get metadata for STC
- % Note that slice ordering is assumed to be from foot to head. If it is not, enter
- % instead: TR - INTRASCAN_TIME - SLICE_TIMING_VECTOR
-
- % SPM accepts slice time acquisition as inputs for slice order (simplifies
- % things when dealing with multiecho data)
- nbSlices = length(sliceOrder); % unique is necessary in case of multi echo
- TR = opt.metadata.RepetitionTime;
- TA = TR - (TR / nbSlices);
-
- maxSliceTime = max(sliceOrder);
- minSliceTime = min(sliceOrder);
- if isempty(opt.STC_referenceSlice)
- referenceSlice = (maxSliceTime - minSliceTime) / 2;
- else
- referenceSlice = opt.STC_referenceSlice;
- end
- if referenceSlice > TA
- error('%s (%f) %s (%f).\n%s', ...
- 'The reference slice time', referenceSlice, ...
- 'is greater than the acquisition time', TA, ...
- ['Reference slice time must be in milliseconds ' ...
- 'or leave it empty to use mid-acquisition time as reference.']);
- end
-
- % prefix of the files to look for
- prefix = getPrefix('STC', opt);
-
- [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
-
- for iSes = 1:nbSessions
-
- % get all runs for that subject across all sessions
- [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
-
- for iRun = 1:nbRuns
-
- % get the filename for this bold run for this task
- [fileName, subFuncDataDir] = getBoldFilename( ...
- BIDS, ...
- subID, sessions{iSes}, runs{iRun}, opt);
-
- % check that the file with the right prefix exist
- files = inputFileValidation(subFuncDataDir, prefix, fileName);
-
- % add the file to the list
- matlabbatch{1}.spm.temporal.st.scans{iRun} = cellstr(files);
-
- disp(files{1});
-
- end
-
- end
-
- matlabbatch{1}.spm.temporal.st.nslices = nbSlices;
- matlabbatch{1}.spm.temporal.st.tr = TR;
- matlabbatch{1}.spm.temporal.st.ta = TA;
- matlabbatch{1}.spm.temporal.st.so = sliceOrder;
- matlabbatch{1}.spm.temporal.st.refslice = referenceSlice;
-
-end
diff --git a/src/spmBatching/setBatchSmoothing.m b/src/spmBatching/setBatchSmoothing.m
deleted file mode 100644
index 02695e1d..00000000
--- a/src/spmBatching/setBatchSmoothing.m
+++ /dev/null
@@ -1,52 +0,0 @@
-function matlabbatch = setBatchSmoothing(BIDS, opt, subID, funcFWHM)
-
- % creates prefix to look for
- prefix = getPrefix('smoothing', opt);
- if strcmp(opt.space, 'T1w')
- prefix = getPrefix('smoothing_space-T1w', opt);
- end
-
- % identify sessions for this subject
- [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
-
- % clear previous matlabbatch and files
- matlabbatch = [];
- allFiles = [];
-
- sesCounter = 1;
-
- for iSes = 1:nbSessions % For each session
-
- % get all runs for that subject across all sessions
- [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
-
- % numRuns = group(iGroup).numRuns(iSub);
- for iRun = 1:nbRuns
-
- % get the filename for this bold run for this task
- [fileName, subFuncDataDir] = getBoldFilename( ...
- BIDS, ...
- subID, sessions{iSes}, runs{iRun}, opt);
-
- % check that the file with the right prefix exist
- files = inputFileValidation(subFuncDataDir, prefix, fileName);
-
- % add the files to list
- allFilesTemp = cellstr(files);
- allFiles = [allFiles; allFilesTemp]; %#ok
- sesCounter = sesCounter + 1;
-
- end
- end
-
- matlabbatch{1}.spm.spatial.smooth.data = allFiles;
- % Define the amount of smoothing required
- matlabbatch{1}.spm.spatial.smooth.fwhm = [funcFWHM funcFWHM funcFWHM];
- matlabbatch{1}.spm.spatial.smooth.dtype = 0;
- matlabbatch{1}.spm.spatial.smooth.im = 0;
-
- % Prefix = s+funcFWHM
- matlabbatch{1}.spm.spatial.smooth.prefix = ...
- [spm_get_defaults('smooth.prefix'), num2str(funcFWHM)];
-
-end
diff --git a/src/spmBatching/setBatchSubjectLevelContrasts.m b/src/spmBatching/setBatchSubjectLevelContrasts.m
deleted file mode 100644
index 42c6cf25..00000000
--- a/src/spmBatching/setBatchSubjectLevelContrasts.m
+++ /dev/null
@@ -1,21 +0,0 @@
-function matlabbatch = setBatchSubjectLevelContrasts(opt, subID, funcFWHM, isMVPA)
-
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
-
- % Create Contrasts
- contrasts = specifyContrasts(ffxDir, opt.taskName, opt, isMVPA);
-
- matlabbatch = [];
-
- for icon = 1:size(contrasts, 2)
- matlabbatch{1}.spm.stats.con.consess{icon}.tcon.name = ...
- contrasts(icon).name;
- matlabbatch{1}.spm.stats.con.consess{icon}.tcon.convec = ...
- contrasts(icon).C;
- matlabbatch{1}.spm.stats.con.consess{icon}.tcon.sessrep = 'none';
- end
-
- matlabbatch{1}.spm.stats.con.spmmat = cellstr(fullfile(ffxDir, 'SPM.mat'));
- matlabbatch{1}.spm.stats.con.delete = 1;
-
-end
diff --git a/src/spmBatching/setBatchSubjectLevelGLMSpec.m b/src/spmBatching/setBatchSubjectLevelGLMSpec.m
deleted file mode 100644
index a9712581..00000000
--- a/src/spmBatching/setBatchSubjectLevelGLMSpec.m
+++ /dev/null
@@ -1,113 +0,0 @@
-function matlabbatch = setBatchSubjectLevelGLMSpec(varargin)
-
- [BIDS, opt, subID, funcFWHM, isMVPA] = deal(varargin{:});
-
- % Check the slice timing information is not in the metadata and not added
- % manually in the opt variable.
- % Necessary to make sure that the reference slice used for slice time
- % correction is the one we center our model on
- sliceOrder = getSliceOrder(opt, 0);
-
- if isempty(sliceOrder)
- % no slice order defined here so we fall back on using the number of
- % slice in the first bold imageBIDS, opt, subID, funcFWHM, iSes, iRun
- % to set the number of time bins we will use to upsample our model
- % during regression creation
- fileName = spm_BIDS(BIDS, 'data', ...
- 'sub', subID, ...
- 'type', 'bold');
- fileName = strrep(fileName{1}, '.gz', '');
- hdr = spm_vol(fileName);
- % we are assuming axial acquisition here
- sliceOrder = 1:hdr(1).dim(3);
- end
-
- %%
- matlabbatch = [];
-
- matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'secs';
-
- % get TR from metadata
- TR = opt.metadata.RepetitionTime;
- matlabbatch{1}.spm.stats.fmri_spec.timing.RT = TR;
-
- % number of times bins
- nbTimeBins = numel(unique(sliceOrder));
- matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = nbTimeBins;
-
- refBin = floor(nbTimeBins / 2);
- matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = refBin;
-
- % Create ffxDir if it doesnt exist
- % If it exists, issue a warning that it has been overwritten
- ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
- if exist(ffxDir, 'dir') %
- warning('overwriting directory: %s \n', ffxDir);
- rmdir(ffxDir, 's');
- mkdir(ffxDir);
- end
- matlabbatch{1}.spm.stats.fmri_spec.dir = {ffxDir};
-
- matlabbatch{1}.spm.stats.fmri_spec.fact = struct('name', {}, 'levels', {});
-
- matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
-
- matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
-
- matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
-
- matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{1}.spm.stats.fmri_spec.cvi = 'AR(1)';
-
- % identify sessions for this subject
- [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
-
- sesCounter = 1;
- for iSes = 1:nbSessions
-
- % get all runs for that subject across all sessions
- [~, nbRuns] = ...
- getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
-
- for iRun = 1:nbRuns
-
- % get functional files
- fullpathBoldFileName = ...
- getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
- matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).scans = ...
- cellstr(fullpathBoldFileName);
-
- % get stimuli onset time file
- fullpathOnsetFileName = ...
- createAndReturnOnsetFile(opt, subID, funcFWHM, fullpathBoldFileName, isMVPA);
-
- matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).multi = ...
- cellstr(fullpathOnsetFileName);
-
- % get realignment parameters
- realignParamFile = ...
- getRealignParamFile(opt, fullpathBoldFileName, funcFWHM);
- matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).multi_reg = ...
- cellstr(realignParamFile);
-
- % multiregressor selection
- matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).regress = ...
- struct('name', {}, 'val', {});
-
- % multicondition selection
- matlabbatch{1}.spm.stats.fmri_spec.sess(sesCounter).cond = ...
- struct('name', {}, 'onset', {}, 'duration', {});
-
- % The following lines are commented out because those parameters
- % can be set in the spm_my_defaults.m
- % matlabbatch{1}.spm.stats.fmri_spec.sess(ses_counter).hpf = 128;
-
- sesCounter = sesCounter + 1;
-
- end
- end
-
-end
diff --git a/src/subject_level/concatBetaImgTmaps.m b/src/subject_level/concatBetaImgTmaps.m
new file mode 100644
index 00000000..982b82ba
--- /dev/null
+++ b/src/subject_level/concatBetaImgTmaps.m
@@ -0,0 +1,122 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function concatBetaImgTmaps(funcFWHM, opt, deleteIndBeta, deleteIndTmaps)
+ % Make 4D images of beta and t-maps for the MVPA. ::
+ %
+ % concatBetaImgTmaps(funcFWHM, opt, [deleteIndBeta = true,] [deleteIndTmaps = true])
+ %
+ % :param funcFWHM: (scalar) smoothing (FWHM) applied to the the normalized EPI
+ % :param opt: (boolean) options structure
+ % :param deleteIndBeta: (boolean)
+ % :param deleteIndTmaps: (boolean) decide to delete t-maps, beta-maps
+ %
+ % If no ``opt`` input is given this function will attempt to load a json file.
+ %
+
+ % delete individual Beta and tmaps
+ if nargin < 3
+ deleteIndBeta = 1;
+ deleteIndTmaps = 1;
+ end
+
+ % load the subjects/Groups information and the task name
+ [group, opt, ~] = getData(opt);
+
+ %% Loop through the groups, subjects
+ for iGroup = 1:length(group)
+
+ for iSub = 1:group(iGroup).numSub
+
+ subID = group(iGroup).subNumber{iSub};
+
+ fprintf(1, 'PREPARING: 4D maps: %s \n', subID);
+
+ ffxDir = getFFXdir(subID, funcFWHM, opt, isMVPA);
+
+ load(fullfile(ffxDir, 'SPM.mat'));
+
+ contrasts = specifyContrasts(ffxDir, opt.taskName, opt, isMVPA);
+
+ beta_maps = cell(length(contrasts), 1);
+ t_maps = cell(length(contrasts), 1);
+
+ % path to beta and t-map files.
+ for iContrast = 1:length(beta_maps)
+ % Note that the betas are created from the idx (Beta_idx(iBeta))
+ fileName = sprintf('beta_%04d.nii', find(contrasts(iContrast).C));
+ fileName = validationInputFile(ffxDir, fileName);
+ beta_maps{iContrast, 1} = [fileName, ',1'];
+
+ % while the contrastes (t-maps) are not from the index. They were created
+ fileName = sprintf('spmT_%04d.nii', iContrast);
+ fileName = validationInputFile(ffxDir, fileName);
+ t_maps{iContrast, 1} = [fileName, ',1'];
+ end
+
+ % clear previous matlabbatch and files
+ matlabbatch = [];
+
+ % 4D beta maps
+ matlabbatch{1}.spm.util.cat.vols = beta_maps;
+ matlabbatch{1}.spm.util.cat.name = ['4D_beta_', num2str(funcFWHM), '.nii'];
+ matlabbatch{1}.spm.util.cat.dtype = 4;
+
+ % 4D t-maps
+ matlabbatch{2}.spm.util.cat.vols = t_maps;
+ matlabbatch{2}.spm.util.cat.name = ['4D_t_maps_', num2str(funcFWHM), '.nii'];
+ matlabbatch{2}.spm.util.cat.dtype = 4;
+
+ saveMatlabBatch(matlabbatch, 'concatBetaImgTmaps', opt, subID);
+
+ spm_jobman('run', matlabbatch);
+
+ removeBetaImgTmaps(beta_maps, t_maps, deleteIndBeta, deleteIndTmaps);
+
+ end
+ end
+
+end
+
+function removeBetaImgTmaps(beta_maps, t_maps, deleteIndBeta, deleteIndTmaps)
+
+ % delete maps
+ if deleteIndBeta
+
+ % delete all individual beta maps
+ fprintf('Deleting individual beta-maps ... ');
+ for iBeta = 1:length(beta_maps)
+ delete(beta_maps{iBeta}(1:end - 2));
+ end
+ fprintf('Done. \n\n\n ');
+
+ end
+
+ if deleteIndTmaps
+
+ % delete all individual con maps
+ fprintf('Deleting individual con maps ... ');
+ for iCon = 1:length(t_maps)
+ delete(fullfile(ffxDir, ['con_', sprintf('%04d', iCon), '.nii']));
+ end
+ fprintf('Done. \n\n\n ');
+
+ % delete all individual t-maps
+ fprintf('Deleting individual t-maps ... ');
+ for iTmap = 1:length(t_maps)
+ delete(t_maps{iTmap}(1:end - 2));
+ end
+ fprintf('Done. \n\n\n ');
+ end
+
+ % delete mat files
+
+ % This is refactorable
+ % ex: delete(fullfile(ffxDir, ['4D_*', num2str(funcFWHM), '.mat']));
+
+ if exist(fullfile(ffxDir, ['4D_beta_', num2str(funcFWHM), '.mat']), 'file')
+ delete(fullfile(ffxDir, ['4D_beta_', num2str(funcFWHM), '.mat']));
+ end
+ if exist(fullfile(ffxDir, ['4D_t_maps_', num2str(funcFWHM), '.mat']), 'file')
+ delete(fullfile(ffxDir, ['4D_t_maps_', num2str(funcFWHM), '.mat']));
+ end
+end
diff --git a/src/subject_level/convertOnsetTsvToMat.m b/src/subject_level/convertOnsetTsvToMat.m
new file mode 100644
index 00000000..1a812360
--- /dev/null
+++ b/src/subject_level/convertOnsetTsvToMat.m
@@ -0,0 +1,74 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function fullpathOnsetFileName = convertOnsetTsvToMat(opt, tsvFile)
+ %% Converts a tsv file to an onset file suitable for SPM ffx analysis
+ % The scripts extracts the conditions' names, onsets, and durations, and
+ % converts them to TRs (time unit) and saves the onset file to be used for
+ % SPM
+ %
+ [pth, file, ext] = spm_fileparts(tsvFile);
+ tsvFile = validationInputFile(pth, [file, ext]);
+
+ % Read the tsv file
+ fprintf('reading the tsv file : %s \n', tsvFile);
+ t = spm_load(tsvFile);
+
+ if ~isfield(t, 'trial_type')
+
+ errorStruct.identifier = 'convertOnsetTsvToMat:noTrialType';
+ errorStruct.message = sprintf('%s\n%s', ...
+ 'There was no trial_type field in this file:', ...
+ tsvFile);
+ error(errorStruct);
+
+ end
+
+ conds = t.trial_type; % assign all the tsv information to a variable called conds.
+
+ % identify where the conditions to include that are specificed in the 'un' step of the
+ % model file
+ model = spm_jsonread(opt.model.file);
+
+ for runIdx = 1:numel(model.Steps)
+ step = model.Steps(runIdx);
+ if iscell(step)
+ step = step{1};
+ end
+ if strcmp(step.Level, 'run')
+ break
+ end
+ end
+
+ isTrialType = strfind(step.Model.X, 'trial_type.');
+
+ % for each condition
+ for iCond = 1:numel(isTrialType)
+
+ if isTrialType{iCond}
+
+ conditionName = strrep(step.Model.X{iCond}, ...
+ 'trial_type.', ...
+ '');
+
+ % Get the index of each condition by comparing the unique names and
+ % each line in the tsv files
+ idx = find(strcmp(conditionName, conds));
+
+ % Get the onset and duration of each condition
+ names{1, iCond} = conditionName;
+ onsets{1, iCond} = t.onset(idx)'; %#ok<*AGROW,*NASGU>
+ durations{1, iCond} = t.duration(idx)';
+
+ end
+ end
+
+ % save the onsets as a matfile
+ [pth, file] = spm_fileparts(tsvFile);
+
+ fullpathOnsetFileName = fullfile(pth, ['onsets_' file '.mat']);
+
+ save(fullpathOnsetFileName, ...
+ 'names', 'onsets', 'durations', ...
+ '-v7');
+
+end
diff --git a/src/subject_level/createAndReturnOnsetFile.m b/src/subject_level/createAndReturnOnsetFile.m
new file mode 100644
index 00000000..9e3f1599
--- /dev/null
+++ b/src/subject_level/createAndReturnOnsetFile.m
@@ -0,0 +1,19 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function onsetFileName = createAndReturnOnsetFile(opt, subID, tsvFile, funcFWHM)
+ % onsetFileName = createAndReturnOnsetFile(opt, boldFileName, prefix, isMVPA)
+ %
+ % gets the tsv onset file based on the bold file name (removes any prefix)
+ %
+ % convert the tsv files to a mat file to be used by SPM
+
+ onsetFileName = convertOnsetTsvToMat(opt, tsvFile);
+
+ % move file into the FFX directory
+ [~, filename, ext] = spm_fileparts(onsetFileName);
+ ffxDir = getFFXdir(subID, funcFWHM, opt);
+ copyfile(onsetFileName, ffxDir);
+
+ onsetFileName = fullfile(ffxDir, [filename ext]);
+
+end
diff --git a/src/subject_level/deleteResidualImages.m b/src/subject_level/deleteResidualImages.m
new file mode 100644
index 00000000..aa51b58a
--- /dev/null
+++ b/src/subject_level/deleteResidualImages.m
@@ -0,0 +1,5 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function deleteResidualImages(ffxDir)
+ delete(fullfile(ffxDir, 'Res_*.nii'));
+end
diff --git a/src/subject_level/getBoldFilenameForFFX.m b/src/subject_level/getBoldFilenameForFFX.m
new file mode 100644
index 00000000..eafd7d72
--- /dev/null
+++ b/src/subject_level/getBoldFilenameForFFX.m
@@ -0,0 +1,29 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function [boldFileName, prefix] = getBoldFilenameForFFX(varargin)
+ % [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun)
+ %
+ % get the filename for this bold run for this task for the FFX setup
+ % and check that the file with the right prefix exist
+
+ [BIDS, opt, subID, funcFWHM, iSes, iRun] = deal(varargin{:});
+
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
+
+ runs = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ prefix = getPrefix('FFX', opt, funcFWHM);
+ if strcmp(opt.space, 'individual')
+ prefix = getPrefix('FFX_space-individual', opt, funcFWHM);
+ end
+
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{iSes}, runs{iRun}, opt);
+
+ boldFileName = validationInputFile( ...
+ subFuncDataDir, ...
+ fileName, ...
+ prefix);
+
+end
diff --git a/src/subject_level/getFFXdir.m b/src/subject_level/getFFXdir.m
new file mode 100644
index 00000000..01d96403
--- /dev/null
+++ b/src/subject_level/getFFXdir.m
@@ -0,0 +1,19 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function ffxDir = getFFXdir(subID, funcFWFM, opt)
+ % ffxDir = getFFXdir(subID, funcFWFM, opt)
+ %
+ % sets the name the FFX directory and creates it if it does not exist
+ %
+ %
+
+ ffxDir = fullfile(opt.derivativesDir, ...
+ ['sub-', subID], ...
+ 'stats', ...
+ ['ffx_task-', opt.taskName], ...
+ ['ffx_space-' opt.space '_FWHM-', num2str(funcFWFM)]);
+
+ if ~exist(ffxDir, 'dir')
+ mkdir(ffxDir);
+ end
+end
diff --git a/src/subject_level/specifyContrasts.m b/src/subject_level/specifyContrasts.m
new file mode 100644
index 00000000..b886f517
--- /dev/null
+++ b/src/subject_level/specifyContrasts.m
@@ -0,0 +1,165 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function contrasts = specifyContrasts(ffxDir, taskName, opt)
+ % Specifies the first level contrasts
+ %
+ % To know the names of the columns of the design matrix, type :
+ % strvcat(SPM.xX.name)
+ %
+ % EXAMPLE
+ % Sn(1) ins 1
+ % Sn(1) ins 2
+ % Sn(1) T1
+ % Sn(1) T2
+ % Sn(1) R1
+ % Sn(1) R2
+ % Sn(1) R3
+ % Sn(1) R4
+ % Sn(1) R5
+ % Sn(1) R6
+
+ load(fullfile(ffxDir, 'SPM.mat'));
+
+ model = spm_jsonread(opt.model.file);
+
+ contrasts = struct('C', [], 'name', []);
+ con_counter = 0;
+
+ % for the task of interest
+ if ~strcmp(model.Input.task, taskName)
+ return
+ end
+
+ % check all the steps specified in the model
+ for iStep = 1:length(model.Steps)
+
+ step = model.Steps(iStep);
+
+ if iscell(step)
+ step = step{1};
+ end
+
+ switch step.Level
+
+ case 'run'
+
+ [contrasts, con_counter] = ...
+ specifyRunLvlContrasts(contrasts, step, con_counter, SPM);
+
+ case 'subject'
+
+ [contrasts, con_counter] = ...
+ specifySubLvlContrasts(contrasts, step, con_counter, SPM);
+
+ end
+
+ end
+
+end
+
+function [cdt_name, regIdx] = getRegIdx(conList, iCon, SPM, iCdt)
+ % get regressors index corresponding to the HRF of of a condition
+
+ if iscell(conList)
+ cdt_name = conList{iCon};
+ elseif isstruct(conList)
+ cdt_name = conList(iCon).ConditionList{iCdt};
+ end
+
+ % get condition name
+ cdt_name = strrep(cdt_name, 'trial_type.', '');
+
+ % get regressors index corresponding to the HRF of that condition
+ regIdx = strfind(SPM.xX.name', [' ' cdt_name '*bf(1)']);
+ regIdx = ~cellfun('isempty', regIdx); %#ok<*STRCL1>
+
+end
+
+function [contrasts, con_counter] = specifySubLvlContrasts(contrasts, Step, con_counter, SPM)
+
+ if isfield(Step, 'AutoContrasts')
+
+ % first the contrasts to compute automatically against baseline
+ for iCon = 1:length(Step.AutoContrasts)
+
+ con_counter = con_counter + 1;
+
+ C = zeros(1, size(SPM.xX.X, 2));
+
+ % get regressors index corresponding to the HRF of that condition
+ [cdt_name, regIdx] = getRegIdx(Step.AutoContrasts, iCon, SPM);
+
+ % give them a value of 1
+ C(end, regIdx) = 1;
+
+ % stores the specification
+ contrasts(con_counter).C = C; %#ok<*AGROW>
+ contrasts(con_counter).name = cdt_name;
+
+ end
+
+ end
+
+ if isfield(Step, 'Contrasts')
+
+ % then the contrasts that involve contrasting conditions
+ % amongst themselves or something inferior to baseline
+ for iCon = 1:length(Step.Contrasts)
+
+ con_counter = con_counter + 1;
+
+ C = zeros(1, size(SPM.xX.X, 2));
+
+ for iCdt = 1:length(Step.Contrasts(iCon).ConditionList)
+
+ % get regressors index corresponding to the HRF of that condition
+ [~, regIdx] = getRegIdx(Step.Contrasts, iCon, SPM, iCdt);
+
+ % give them a value of 1
+ C(end, regIdx) = Step.Contrasts(iCon).weights(iCdt);
+
+ end
+
+ % stores the specification
+ contrasts(con_counter).C = C;
+ contrasts(con_counter).name = ...
+ Step.Contrasts(iCon).Name;
+
+ end
+
+ end
+
+end
+
+function [contrasts, con_counter] = specifyRunLvlContrasts(contrasts, Step, con_counter, SPM)
+
+ if isfield(Step, 'AutoContrasts')
+
+ % first the contrasts to compute automatically against baseline
+ for iCon = 1:length(Step.AutoContrasts)
+
+ % get regressors index corresponding to the HRF of that condition
+ [cdt_name, regIdx] = getRegIdx(Step.AutoContrasts, iCon, SPM);
+
+ regIdx = find(regIdx);
+
+ % For each event of each condition, create a seperate
+ % contrast
+ for iReg = 1:length(regIdx)
+
+ C = zeros(1, size(SPM.xX.X, 2));
+
+ % add a new line for a new contrast
+ con_counter = con_counter + 1;
+ C(end, regIdx(iReg)) = 1; % give each event a value of 1
+
+ % stores the specification
+ contrasts(con_counter).C = C;
+ contrasts(con_counter).name = [cdt_name, '_', num2str(iReg)];
+
+ end
+ end
+
+ end
+
+end
diff --git a/src/templates/templateFunction.m b/src/templates/templateFunction.m
new file mode 100644
index 00000000..6897f2f2
--- /dev/null
+++ b/src/templates/templateFunction.m
@@ -0,0 +1,29 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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
+
+ % The code goes below
+
+end
diff --git a/src/templates/templateFunctionExample.m b/src/templates/templateFunctionExample.m
new file mode 100644
index 00000000..941690b5
--- /dev/null
+++ b/src/templates/templateFunctionExample.m
@@ -0,0 +1,42 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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
+
+ % The code goes below
+
+end
diff --git a/src/templates/templateFunctionNumpy.m b/src/templates/templateFunctionNumpy.m
new file mode 100644
index 00000000..fed5063d
--- /dev/null
+++ b/src/templates/templateFunctionNumpy.m
@@ -0,0 +1,21 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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
+
+ % The code goes below
+
+end
diff --git a/src/templates/templateGetOption.m b/src/templates/templateGetOption.m
new file mode 100644
index 00000000..4946a976
--- /dev/null
+++ b/src/templates/templateGetOption.m
@@ -0,0 +1,71 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+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/src/unzipImgAndReturnsFullpathName.m b/src/unzipImgAndReturnsFullpathName.m
new file mode 100644
index 00000000..865687e8
--- /dev/null
+++ b/src/unzipImgAndReturnsFullpathName.m
@@ -0,0 +1,20 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function unzippedFullpathImgName = unzipImgAndReturnsFullpathName(fullpathImgName)
+ % unzippedFullpathImgName = unzipImgAndReturnsFullpathName(fullpathImgName)
+ %
+ %
+
+ [directory, filename, ext] = spm_fileparts(fullpathImgName);
+
+ if strcmp(ext, '.gz')
+ % unzip nii.gz structural file to be read by SPM
+ fullpathImgName = load_untouch_nii(fullpathImgName);
+ save_untouch_nii(fullpathImgName, fullfile(directory, filename));
+ [unzippedFullpathImgName] = fullfile(directory, filename);
+
+ else
+ [unzippedFullpathImgName] = fullfile(directory, [filename ext]);
+
+ end
+end
diff --git a/src/utils/checkDependencies.m b/src/utils/checkDependencies.m
index c6962842..2cb74613 100644
--- a/src/utils/checkDependencies.m
+++ b/src/utils/checkDependencies.m
@@ -1,44 +1,47 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
function checkDependencies()
- % Checks that that the right dependencies are installed. ALso loads the spm defaults.
-
- printCredits();
-
- SPM_main = 'SPM12';
- SPM_sub = '7487';
-
- nifti_tools_url = ...
- ['https://www.mathworks.com/matlabcentral/fileexchange/' ...
- '8797-tools-for-nifti-and-analyze-image'];
-
- fprintf('Checking dependencies\n');
-
- % check spm version
- try
- [a, b] = spm('ver');
- fprintf(' Using %s %s\n', a, b);
- if any(~[strcmp(a, SPM_main) strcmp(b, SPM_sub)])
- str = sprintf('%s %s %s.\n%s', ...
- 'The current version SPM version is not', SPM_main, SPM_sub, ...
- 'In case of problems (e.g json file related) consider updating.');
- warning(str); %#ok<*SPWRN>
- end
- catch
- error('Failed to check the SPM version: Are you sure that SPM is in the matlab path?');
- end
- spm('defaults', 'fmri');
-
- % Check the Nifti tools are indeed there.
- a = which('load_untouch_nii');
- if isempty(a)
- errorStruct.identifier = 'checkDependencies:missingDependency';
- errorStruct.message = sprintf('%s \n%s', ...
- 'Failed to find the Nifti tools: Are you sure they in the matlab path?', ...
- 'You can download them here: %s', nifti_tools_url);
- error(errorStruct);
- else
- fprintf(' Nifti tools detected\n');
- end
+ % Checks that that the right dependencies are installed. ALso loads the spm defaults.
+
+ printCredits();
- fprintf(' We got all we need. Let''s get to work.\n');
+ SPM_main = 'SPM12';
+ SPM_sub = '7487';
+
+ nifti_tools_url = ...
+ ['https://www.mathworks.com/matlabcentral/fileexchange/' ...
+ '8797-tools-for-nifti-and-analyze-image'];
+
+ fprintf('Checking dependencies\n');
+
+ % check spm version
+ try
+ [a, b] = spm('ver');
+ fprintf(' Using %s %s\n', a, b);
+ if any(~[strcmp(a, SPM_main) strcmp(b, SPM_sub)])
+ str = sprintf('%s %s %s.\n%s', ...
+ 'The current version SPM version is not', SPM_main, SPM_sub, ...
+ 'In case of problems (e.g json file related) consider updating.');
+ warning(str); %#ok<*SPWRN>
+ end
+ catch
+ error('Failed to check the SPM version: Are you sure that SPM is in the matlab path?');
+ end
+ spm('defaults', 'fmri');
+
+ % Check the Nifti tools are indeed there.
+ a = which('load_untouch_nii');
+ if isempty(a)
+ errorStruct.identifier = 'checkDependencies:missingDependency';
+ errorStruct.message = sprintf('%s \n%s', ...
+ ['Failed to find the Nifti tools: ' ...
+ 'Are you sure they in the matlab path?'], ...
+ 'You can download them here: %s', nifti_tools_url);
+ error(errorStruct);
+ else
+ fprintf(' Nifti tools detected\n');
+ end
+
+ fprintf(' We got all we need. Let''s get to work.\n');
end
diff --git a/src/utils/checkOptions.m b/src/utils/checkOptions.m
deleted file mode 100644
index 72d0777f..00000000
--- a/src/utils/checkOptions.m
+++ /dev/null
@@ -1,84 +0,0 @@
-function opt = checkOptions(opt)
- % opt = checkOptions(opt)
- %
- % we check the option inputs and add any missing field with some defaults
-
- if nargin < 1
- opt = struct();
- end
-
- fieldsToSet = setDefaultOption();
-
- opt = setDefaultFields(opt, fieldsToSet);
-
- 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;
-
- % task to analyze
- fieldsToSet.taskName = '';
-
- % space where we conduct the analysis
- fieldsToSet.space = 'MNI';
-
- % The directory where the derivatives are located
- fieldsToSet.dataDir = '';
-
- % fieldsToSet for slice time correction
- 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 = [];
-
- % Suffix output directory for the saved jobs
- fieldsToSet.jobsDir = '';
-
- % specify the model file that contains the contrasts to compute
- fieldsToSet.contrastList = {};
- fieldsToSet.model.file = '';
-
- % specify the results to compute
- fieldsToSet.result.Steps = [];
-end
diff --git a/src/utils/createDataDictionary.m b/src/utils/createDataDictionary.m
new file mode 100644
index 00000000..3d8c1a55
--- /dev/null
+++ b/src/utils/createDataDictionary.m
@@ -0,0 +1,50 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function createDataDictionary(subFuncDataDir, fileName, nbColums)
+
+ namecColumns = { ...
+ 'trans_x', ...
+ 'trans_y', ...
+ 'trans_z', ...
+ 'rot_x', ...
+ 'rot_y', ...
+ 'rot_z', ...
+ 'trans_x_derivative1', ...
+ 'trans_y_derivative1', ...
+ 'trans_z_derivative1', ...
+ 'rot_x_derivative1', ...
+ 'rot_y_derivative1', ...
+ 'rot_z_derivative1', ...
+ 'trans_x_power2', ...
+ 'trans_y_power2', ...
+ 'trans_z_power2', ...
+ 'rot_x_power2', ...
+ 'rot_y_power2', ...
+ 'rot_z_power2', ...
+ 'trans_x_derivative1_power2', ...
+ 'trans_y_derivative1_power2', ...
+ 'trans_z_derivative1_power2', ...
+ 'rot_x_derivative1_power2', ...
+ 'rot_y_derivative1_power2', ...
+ 'rot_z_derivative1_power2'};
+
+ for iExtraRegressor = 1:nbColums
+ namecColumns{end + 1} = sprintf('censoring_regressor_%i', iExtraRegressor); %#ok<*AGROW>
+ end
+
+ dataDictionary = struct( ...
+ 'Columns', []);
+
+ for iColumns = 1:numel(namecColumns)
+ dataDictionary.Columns{iColumns} = namecColumns{iColumns};
+ end
+
+ spm_jsonwrite( ...
+ fullfile( ...
+ subFuncDataDir, ...
+ strrep(fileName, ...
+ '_bold.nii', ...
+ '_desc-confounds_regressors.json')), ...
+ dataDictionary, ...
+ struct('indent', ' '));
+end
diff --git a/src/utils/getEnvInfo.m b/src/utils/getEnvInfo.m
index b4a1ae9f..a7b16bd4 100644
--- a/src/utils/getEnvInfo.m
+++ b/src/utils/getEnvInfo.m
@@ -1,69 +1,71 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
function [OS, GeneratedBy] = getEnvInfo()
- GeneratedBy(1).name = 'CPP_BIDS_SPM_pipeline';
- GeneratedBy(1).Version = getVersion();
- GeneratedBy(1).Description = '';
- GeneratedBy(1).CodeURL = '';
+ GeneratedBy(1).name = 'CPP_BIDS_SPM_pipeline';
+ GeneratedBy(1).Version = getVersion();
+ GeneratedBy(1).Description = '';
+ GeneratedBy(1).CodeURL = '';
- runsOn = 'Matlab';
- if isOctave
- runsOn = 'Octave';
- end
+ runsOn = 'Matlab';
+ if isOctave
+ runsOn = 'Octave';
+ end
- OS.name = computer();
- OS.platform.name = runsOn;
- OS.platform.version = version();
+ OS.name = computer();
+ OS.platform.name = runsOn;
+ OS.platform.version = version();
- [a, b] = spm('ver');
- OS.spmVersion = [a ' - ' b];
+ [a, b] = spm('ver');
+ OS.spmVersion = [a ' - ' b];
- % if ispc()
- % cmd = 'set';
- % else
- % cmd = 'env';
- % end
- % [~, OS.environmentVariables] = system(cmd);
+ % if ispc()
+ % cmd = 'set';
+ % else
+ % cmd = 'env';
+ % end
+ % [~, OS.environmentVariables] = system(cmd);
- [keys, vals] = getenvall('system');
- OS.environmentVariables.keys = keys;
- OS.environmentVariables.values = vals;
+ [keys, vals] = getenvall('system');
+ OS.environmentVariables.keys = keys;
+ OS.environmentVariables.values = vals;
end
function [keys, vals] = getenvall(method)
- % from
- % https://stackoverflow.com/questions/20004955/list-all-environment-variables-in-matlab
- if nargin < 1
- method = 'system';
- end
- method = validatestring(method, {'java', 'system'});
+ % from
+ % https://stackoverflow.com/questions/20004955/list-all-environment-variables-in-matlab
+ if nargin < 1
+ method = 'system';
+ end
+ method = validatestring(method, {'java', 'system'});
- switch method
- case 'java'
- map = java.lang.System.getenv(); % returns a Java map
- keys = cell(map.keySet.toArray());
- vals = cell(map.values.toArray());
- case 'system'
- if ispc()
- % cmd = 'set "'; %HACK for hidden variables
- cmd = 'set';
- else
- cmd = 'env';
- end
- [~, out] = system(cmd);
- vars = regexp(strtrim(out), '^(.*)=(.*)$', ...
- 'tokens', 'lineanchors', 'dotexceptnewline');
- vars = vertcat(vars{:});
- keys = vars(:, 1);
- vals = vars(:, 2);
- end
+ switch method
+ case 'java'
+ map = java.lang.System.getenv(); % returns a Java map
+ keys = cell(map.keySet.toArray());
+ vals = cell(map.values.toArray());
+ case 'system'
+ if ispc()
+ % cmd = 'set "'; %HACK for hidden variables
+ cmd = 'set';
+ else
+ cmd = 'env';
+ end
+ [~, out] = system(cmd);
+ vars = regexp(strtrim(out), '^(.*)=(.*)$', ...
+ 'tokens', 'lineanchors', 'dotexceptnewline');
+ vars = vertcat(vars{:});
+ keys = vars(:, 1);
+ vals = vars(:, 2);
+ end
- % Windows environment variables are case-insensitive
- if ispc()
- keys = upper(keys);
- end
+ % Windows environment variables are case-insensitive
+ if ispc()
+ keys = upper(keys);
+ end
- % sort alphabetically
- [keys, ord] = sort(keys);
- vals = vals(ord);
+ % sort alphabetically
+ [keys, ord] = sort(keys);
+ vals = vals(ord);
end
diff --git a/src/utils/getVersion.m b/src/utils/getVersion.m
index 0b439272..66536823 100644
--- a/src/utils/getVersion.m
+++ b/src/utils/getVersion.m
@@ -1,8 +1,10 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
function versionNumber = getVersion()
- try
- versionNumber = fileread(fullfile(fileparts(mfilename('fullpath')), ...
- '..', '..', 'version.txt'));
- catch
- versionNumber = 'v0.0.3';
- end
+ try
+ versionNumber = fileread(fullfile(fileparts(mfilename('fullpath')), ...
+ '..', '..', 'version.txt'));
+ catch
+ versionNumber = 'v0.0.3';
+ end
end
diff --git a/src/utils/inputFileValidation.m b/src/utils/inputFileValidation.m
deleted file mode 100644
index 09947010..00000000
--- a/src/utils/inputFileValidation.m
+++ /dev/null
@@ -1,17 +0,0 @@
-function file = inputFileValidation(dir, prefix, fileName)
- file2Process = spm_select('FPList', dir, ['^' prefix fileName '$']);
-
- if isempty(file2Process)
-
- errorStruct.identifier = 'inputFileValidation:nonExistentFile';
- errorStruct.message = sprintf( ...
- 'This file does not exist: %s', ...
- fullfile(dir, [prefix fileName '[.gz]']));
- error(errorStruct);
-
- else
- file{1, 1} = file2Process;
-
- end
-
-end
diff --git a/src/utils/isOctave.m b/src/utils/isOctave.m
index b71f6a0b..7f3cea40 100644
--- a/src/utils/isOctave.m
+++ b/src/utils/isOctave.m
@@ -1,10 +1,13 @@
+% (C) Copyright 2020 Agah Karakuzu
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
function retval = isOctave
- % Return: true if the environment is Octave.
- persistent cacheval % speeds up repeated calls
+ % Return: true if the environment is Octave.
+ persistent cacheval % speeds up repeated calls
- if isempty (cacheval)
- cacheval = (exist ('OCTAVE_VERSION', 'builtin') > 0);
- end
+ if isempty (cacheval)
+ cacheval = (exist ('OCTAVE_VERSION', 'builtin') > 0);
+ end
- retval = cacheval;
+ retval = cacheval;
end
diff --git a/src/utils/loadAndCheckOptions.m b/src/utils/loadAndCheckOptions.m
new file mode 100644
index 00000000..df30316d
--- /dev/null
+++ b/src/utils/loadAndCheckOptions.m
@@ -0,0 +1,40 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function opt = loadAndCheckOptions(opt)
+ % opt = loadAndCheckOptions(opt)
+ %
+ % if not argument is provived checks in the current directory for
+ % ``opt_task-*.json`` files and loads the most recent one by name
+ % (using the date- key).
+ %
+ % then checks the content of the opt structure and adds missing information
+
+ if nargin < 1 || isempty(opt)
+ opt = spm_select('FPList', pwd, '^options_task-.*.json$');
+ end
+
+ if isstruct(opt)
+ opt = checkOptions(opt);
+ fprintf(1, '\nOptions are locked & loaded.\n\n');
+ 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
+
+ if ischar(opt) && exist(opt, 'file')
+ fprintf(1, '\nReading option from: %s.\n', opt);
+ opt = spm_jsonread(opt);
+ else
+ error('the requested file does not exist: %s', opt);
+ end
+
+ opt = checkOptions(opt);
+ fprintf(1, '\nOptions are locked & loaded.\n\n');
+
+end
diff --git a/src/utils/printCredits.m b/src/utils/printCredits.m
index ec8f180c..01ad0812 100644
--- a/src/utils/printCredits.m
+++ b/src/utils/printCredits.m
@@ -1,44 +1,47 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
function printCredits()
- versionNumber = getVersion();
+ versionNumber = getVersion();
- contributors = { ...
- 'Mohamed Rezk', ...
- 'Rémi Gau', ...
- 'Olivier Collignon', ...
- 'Ane Gurtubay', ...
- 'Marco Barilari'};
+ contributors = { ...
+ 'Mohamed Rezk', ...
+ 'Rémi Gau', ...
+ 'Olivier Collignon', ...
+ 'Ane Gurtubay', ...
+ 'Marco Barilari', ...
+ 'Ceren Battal'};
- DOI_URL = 'https://doi.org/10.5281/zenodo.3554331.';
+ DOI_URL = 'https://doi.org/10.5281/zenodo.3554331.';
- repoURL = 'https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline';
+ repoURL = 'https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline';
- disp('___________________________________________________________________________');
- disp('___________________________________________________________________________');
- disp(' ');
- disp(' __ ____ ____ _ _ _ ');
- disp(' / _)( _ \( _ \ | | / \ | ) ');
- disp(' ( (_ )___/ )___/ | |_ / _ \ | \ ');
- disp(' \__)(__) (__) |___||_/ \_||__)');
- disp(' ');
+ disp('___________________________________________________________________________');
+ disp('___________________________________________________________________________');
+ disp(' ');
+ disp(' __ ____ ____ _ _ _ ');
+ disp(' / _)( _ \( _ \ | | / \ | ) ');
+ disp(' ( (_ )___/ )___/ | |_ / _ \ | \ ');
+ disp(' \__)(__) (__) |___||_/ \_||__)');
+ disp(' ');
- splash = 'Thank you for using the CPP lap pipeline - version %s. ';
- fprintf(splash, versionNumber);
- fprintf('\n\n');
+ splash = 'Thank you for using the CPP lap pipeline - version %s. ';
+ fprintf(splash, versionNumber);
+ fprintf('\n\n');
- fprintf('Current list of contributors includes\n');
- for iCont = 1:numel(contributors)
- fprintf(' %s\n', contributors{iCont});
- end
- fprintf('\b\n\n');
+ fprintf('Current list of contributors includes\n');
+ for iCont = 1:numel(contributors)
+ fprintf(' %s\n', contributors{iCont});
+ end
+ fprintf('\b\n\n');
- fprintf('Please cite using the following DOI: \n %s\n\n', DOI_URL);
+ fprintf('Please cite using the following DOI: \n %s\n\n', DOI_URL);
- fprintf('For bug report & suggestions see our github repo: \n %s\n\n', repoURL);
+ fprintf('For bug report & suggestions see our github repo: \n %s\n\n', repoURL);
- disp('___________________________________________________________________________');
- disp('___________________________________________________________________________');
+ disp('___________________________________________________________________________');
+ disp('___________________________________________________________________________');
- fprintf('\n\n');
+ fprintf('\n\n');
end
diff --git a/src/utils/printProcessingRun.m b/src/utils/printProcessingRun.m
new file mode 100644
index 00000000..29769001
--- /dev/null
+++ b/src/utils/printProcessingRun.m
@@ -0,0 +1,14 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function printProcessingRun(groupName, iSub, subID, iSes, iRun)
+
+ fprintf(1, ...
+ [ ...
+ ' PROCESSING GROUP: %s' ...
+ 'SUBJECT No.: %i ' ...
+ 'SUBJECT ID : %s ' ...
+ 'SESSION: %i ' ...
+ 'RUN: %i \n'], ...
+ groupName, iSub, subID, iSes, iRun);
+
+end
diff --git a/src/utils/printProcessingSubject.m b/src/utils/printProcessingSubject.m
new file mode 100644
index 00000000..cb5a27a5
--- /dev/null
+++ b/src/utils/printProcessingSubject.m
@@ -0,0 +1,11 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function printProcessingSubject(groupName, iSub, subID)
+
+ fprintf(1, [ ...
+ ' PROCESSING GROUP: %s' ...
+ 'SUBJECT No.: %i ' ...
+ 'SUBJECT ID : %s \n'], ...
+ groupName, iSub, subID);
+
+end
diff --git a/src/utils/saveMatlabBatch.m b/src/utils/saveMatlabBatch.m
new file mode 100644
index 00000000..79d1eb68
--- /dev/null
+++ b/src/utils/saveMatlabBatch.m
@@ -0,0 +1,52 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function saveMatlabBatch(matlabbatch, batchType, opt, subID)
+ % saveMatlabBatch(batch, batchType, opt, subID)
+ %
+ % Also save some basic environnment info
+ %
+ % batch : matlabbatch
+ % batchType: (string) name to give to the batch file
+ %
+ %
+
+ if nargin < 4 || isempty(subID)
+ subID = 'group';
+ else
+ subID = ['sub-' subID];
+ end
+
+ jobsDir = fullfile(opt.jobsDir, subID);
+ [~, ~, ~] = mkdir(jobsDir);
+
+ filename = sprintf( ...
+ '%s_jobs_matlabbatch_SPM12_%s.mat', ...
+ datestr(now, 'yyyymmdd_HHMM'), ...
+ batchType);
+
+ [OS, GeneratedBy] = getEnvInfo();
+ GeneratedBy(1).Description = batchType;
+
+ save(fullfile(jobsDir, filename), 'matlabbatch', '-v7');
+
+ % write as json for more "human readibility"
+ opts.indent = ' ';
+
+ json.matlabbach = matlabbatch;
+ json.GeneratedBy = GeneratedBy;
+ json.OS = OS;
+
+ try
+ spm_jsonwrite( ...
+ fullfile(jobsDir, strrep(filename, '.mat', '.json')), ...
+ json, opts);
+ catch
+ % if we have a dependendy object in the batch then we can't save the
+ % batch structure as a json
+ json = rmfield(json, 'matlabbach');
+ spm_jsonwrite( ...
+ fullfile(jobsDir, strrep(filename, '.mat', '.json')), ...
+ json, opts);
+ end
+
+end
diff --git a/src/utils/saveOptions.m b/src/utils/saveOptions.m
new file mode 100644
index 00000000..78585d90
--- /dev/null
+++ b/src/utils/saveOptions.m
@@ -0,0 +1,15 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function saveOptions(opt)
+
+ filename = fullfile(pwd, ['options', ...
+ '_task-', opt.taskName, ...
+ '_date-' datestr(now, 'yyyymmddHHMM'), ...
+ '.json']);
+
+ jsonFormat.indent = ' ';
+ spm_jsonwrite(filename, opt, jsonFormat);
+
+ fprintf('Options saved in: \n\n');
+
+end
diff --git a/src/utils/setDefaultFields.m b/src/utils/setDefaultFields.m
index bf0e6c47..d79505ce 100644
--- a/src/utils/setDefaultFields.m
+++ b/src/utils/setDefaultFields.m
@@ -1,40 +1,46 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
function structure = setDefaultFields(structure, fieldsToSet)
- % structure = setDefaultFields(structure, fieldsToSet)
- %
- % recursively loop through the fields of a structure and sets a value if they don't exist
- %
+ % structure = setDefaultFields(structure, fieldsToSet)
+ %
+ % recursively loop through the fields of a structure and sets a value if they don't exist
+ %
- if isempty(fieldsToSet)
- return
- end
+ if isempty(fieldsToSet)
+ return
+ end
+
+ names = fieldnames(fieldsToSet);
- names = fieldnames(fieldsToSet);
+ for j = 1:numel(structure)
for i = 1:numel(names)
- thisField = fieldsToSet.(names{i});
+ thisField = fieldsToSet.(names{i});
- if isfield(structure, names{i}) && isstruct(structure.(names{i}))
+ if isfield(structure(j), names{i}) && isstruct(structure(j).(names{i}))
- structure.(names{i}) = ...
- setDefaultFields(structure.(names{i}), fieldsToSet.(names{i}));
+ structure(j).(names{i}) = ...
+ setDefaultFields(structure(j).(names{i}), fieldsToSet.(names{i}));
- else
+ else
- structure = setFieldToIfNotPresent( ...
- structure, ...
- names{i}, ...
- thisField);
- end
+ structure = setFieldToIfNotPresent( ...
+ structure, ...
+ names{i}, ...
+ thisField);
+ end
end
structure = orderfields(structure);
+ end
+
end
function structure = setFieldToIfNotPresent(structure, fieldName, value)
- if ~isfield(structure, fieldName)
- structure.(fieldName) = value;
- end
+ if ~isfield(structure, fieldName)
+ structure.(fieldName) = value;
+ end
end
diff --git a/src/utils/setGraphicWindow.m b/src/utils/setGraphicWindow.m
new file mode 100644
index 00000000..180c3274
--- /dev/null
+++ b/src/utils/setGraphicWindow.m
@@ -0,0 +1,19 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function [interactiveWindow, graphWindow, cmdLine] = setGraphicWindow()
+
+ interactiveWindow = [];
+ graphWindow = [];
+ cmdLine = true;
+
+ if ~spm('CmdLine') && ~isOctave
+
+ try
+ [interactiveWindow, graphWindow, cmdLine] = spm('FnUIsetup');
+ catch
+ warning('Could not open a graphic window. No figure will be created.');
+ end
+
+ end
+
+end
diff --git a/src/utils/validationInputFile.m b/src/utils/validationInputFile.m
new file mode 100644
index 00000000..2a362cda
--- /dev/null
+++ b/src/utils/validationInputFile.m
@@ -0,0 +1,29 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function files = validationInputFile(dir, fileName, prefix)
+ % file = validationInputFile(dir, prefix, fileName)
+ %
+ % Checks if files exist. A prefix can be added. The prefix allows for the
+ % use of regular expression.
+ %
+ % TPMs = validationInputFile(anatDataDir, anatImage, 'c[12]');
+ %
+ % If the filet(s) exist(s), it returns a char array containing list of fullpath.
+
+ if nargin < 3
+ prefix = '';
+ end
+
+ files = spm_select('FPList', dir, ['^' prefix fileName '$']);
+
+ if isempty(files)
+
+ errorStruct.identifier = 'validationInputFile:nonExistentFile';
+ errorStruct.message = sprintf( ...
+ 'This file does not exist: %s', ...
+ fullfile(dir, [prefix fileName '[.gz]']));
+ error(errorStruct);
+
+ end
+
+end
diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m
new file mode 100644
index 00000000..06d5ca42
--- /dev/null
+++ b/src/workflows/bidsCopyRawFolder.m
@@ -0,0 +1,184 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+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.
+ %
+ % USAGE::
+ %
+ % bidsCopyRawFolder([opt,] ...
+ % [deleteZippedNii = true,] ...
+ % [modalitiesToCopy = {'anat', 'func', 'fmap'}])
+ %
+ % :param opt:
+ % :type opt: type
+ % :param deleteZippedNii:
+ % :type deleteZippedNii: boolean
+ % :param modalitiesToCopy:
+ % :type modalitiesToCopy: cell
+ %
+
+ %% input variables default values
+
+ 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 || isempty(opt)
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ %% All tasks in this experiment
+ % raw directory and derivatives directory
+ opt = setDerivativesDir(opt);
+ rawDir = opt.dataDir;
+ derivativesDir = opt.derivativesDir;
+
+ createDerivativeDir(derivativesDir);
+
+ copyTsvJson(rawDir, derivativesDir);
+
+ %% Loop through the groups, subjects, sessions
+ [group, opt, BIDS] = getData(opt, rawDir);
+
+ for iGroup = 1:length(group)
+
+ for iSub = 1:group(iGroup).numSub
+
+ subID = group(iGroup).subNumber{iSub};
+
+ % the folder containing the subjects data
+ subDir = ['sub-', subID];
+
+ mkdir(fullfile(derivativesDir, subDir));
+
+ [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions');
+
+ %% copy the whole subject's folder
+ % use a call to system cp function to use the derefence option (-L)
+ % to get the data 'out' of an eventual datalad dataset
+
+ for iSes = 1:nbSessions
+
+ sessionDir = [];
+ if ~isempty(sessions{iSes})
+ sessionDir = ['ses-' sessions{iSes}];
+ end
+
+ mkdir(fullfile(derivativesDir, subDir, sessionDir));
+
+ 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, sessionDir, modalities{iModality}));
+
+ srcFolder = fullfile(rawDir, ...
+ subDir, ...
+ sessionDir, ...
+ modalities{iModality});
+ targetFolder = fullfile(derivativesDir, ...
+ subDir, ...
+ sessionDir);
+
+ copyModalityDir(srcFolder, targetFolder);
+
+ end
+
+ fprintf('folder copied: %s \n', subDir);
+
+ end
+ 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 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$');
+
+ for iFile = 1:size(zippedNiifiles, 1)
+
+ file = deblank(zippedNiifiles(iFile, :));
+
+ n = load_untouch_nii(file); % load the nifti image
+ save_untouch_nii(n, file(1:end - 4)); % Save the functional data as unzipped nii
+ fprintf('unzipped: %s \n', file);
+
+ if deleteZippedNii
+ delete(file); % delete original zipped file
+ end
+
+ end
+
+end
diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m
new file mode 100644
index 00000000..d6f98e9a
--- /dev/null
+++ b/src/workflows/bidsCreateVDM.m
@@ -0,0 +1,51 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function bidsCreateVDM(opt)
+ % 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);
+
+ 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);
+
+ matlabbatch = setBatchCoregistrationFmap(BIDS, opt, subID);
+ saveMatlabBatch(matlabbatch, 'coregister_fmap', opt, subID);
+ spm_jobman('run', matlabbatch);
+
+ matlabbatch = setBatchCreateVDMs(BIDS, opt, subID);
+ saveMatlabBatch(matlabbatch, 'create_vdm', opt, subID);
+ spm_jobman('run', matlabbatch);
+
+ % TODO
+ % delete temporary mean images ??
+
+ end
+
+ end
+
+ end
+end
diff --git a/src/workflows/bidsFFX.m b/src/workflows/bidsFFX.m
new file mode 100644
index 00000000..c06a36cc
--- /dev/null
+++ b/src/workflows/bidsFFX.m
@@ -0,0 +1,88 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function bidsFFX(action, opt, funcFWHM)
+ % This scripts builds up the design matrix for each subject.
+ % It has to be run in 2 separate steps (action). ::
+ %
+ % bidsFFX(action, funcFWHM, opt)
+ %
+ % :param action: (string) ``specifyAndEstimate`` or ``contrasts``.
+ % :param opt: (scalar) options (see checkOptions())
+ % :param funcFWHM: (scalar) Gaussian kernel size applied to the functional data.
+ %
+ % ``specifyAndEstimate`` for fMRI design + estimate and
+ % ``contrasts`` to estimate contrasts.
+ %
+ % For unsmoothed data ``funcFWHM = 0``, for smoothed data ``funcFWHM = ... mm``.
+ % In this way we can make multiple ffx for different smoothing degrees
+ %
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 3
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % 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)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ switch action
+
+ case 'specifyAndEstimate'
+
+ matlabbatch = setBatchSubjectLevelGLMSpec( ...
+ BIDS, opt, subID, funcFWHM);
+
+ matlabbatch = setFmriEstimateBatch(matlabbatch);
+
+ saveMatlabBatch(matlabbatch, ...
+ ['specify_estimate_ffx_task-', opt.taskName, ...
+ '_space-', opt.space, ...
+ '_FWHM-', num2str(funcFWHM)], ...
+ opt, subID);
+
+ case 'contrasts'
+
+ matlabbatch = setBatchSubjectLevelContrasts(opt, subID, funcFWHM);
+
+ saveMatlabBatch(matlabbatch, ...
+ ['contrasts_ffx_task-', opt.taskName, ...
+ '_space-', opt.space, ...
+ '_FWHM-', num2str(funcFWHM)], ...
+ opt, subID);
+
+ end
+
+ spm_jobman('run', matlabbatch);
+
+ end
+ end
+
+end
+
+function matlabbatch = setFmriEstimateBatch(matlabbatch)
+
+ fprintf(1, 'BUILDING JOB : FMRI estimate\n');
+
+ matlabbatch{2}.spm.stats.fmri_est.spmmat(1) = cfg_dep( ...
+ 'fMRI model specification SPM file', ...
+ substruct( ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'spmmat'));
+
+ matlabbatch{2}.spm.stats.fmri_est.method.Classical = 1;
+ matlabbatch{2}.spm.stats.fmri_est.write_residuals = 1;
+end
diff --git a/src/workflows/bidsRFX.m b/src/workflows/bidsRFX.m
new file mode 100644
index 00000000..a32a36b3
--- /dev/null
+++ b/src/workflows/bidsRFX.m
@@ -0,0 +1,170 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function bidsRFX(action, funcFWHM, conFWHM, opt)
+ %
+ % This script smooth all con images created at the fisrt level in each
+ % subject, create a mean structural image and mean mask over the
+ % population, process the factorial design specification and estimation
+ % and estimate Contrats. ::
+ %
+ % bidsRFX(action, funcFWHM, conFWHM, opt)
+ %
+ % :param action: (string) ``smoothContrasts`` or ``RFX``
+ % :param funcFWHM: (scalar)
+ % :param conFWHM: (scalar)
+ % :param opt: (structure) (see checkOptions())
+ %
+ % - case 'smoothContrasts': smooth con images
+ % - case 'RFX': Mean Struct, MeanMask, Factorial design specification and
+ % estimation, Contrast estimation
+ %
+ % funcFWHM: How much smoothing was applied to the functional
+ % data in the preprocessing
+ %
+ % conFWHM: How much smoothing is required for the CON images for
+ % the second level analysis
+
+ if nargin < 2 || isempty(funcFWHM)
+ funcFWHM = 0;
+ end
+
+ if nargin < 3 || isempty(conFWHM)
+ conFWHM = 0;
+ end
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 4
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % load the subjects/Groups information and the task name
+ [group, opt, ~] = getData(opt);
+
+ switch action
+
+ case 'smoothContrasts'
+
+ matlabbatch = setBatchSmoothConImages(group, funcFWHM, conFWHM, opt);
+
+ saveMatlabBatch( ...
+ ['smooth_con_FWHM-', num2str(conFWHM), '_task-', opt.taskName], ...
+ 'STC', ...
+ opt);
+
+ spm_jobman('run', matlabbatch);
+
+ case 'RFX'
+
+ fprintf(1, 'Create Mean Struct and Mask IMAGES...');
+
+ rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
+
+ % ------
+ % TODO
+ % - need to rethink where to save the anat and mask
+ % - need to smooth the anat
+ % - create a masked version of the anat too
+ % ------
+
+ matlabbatch = ...
+ setBatchMeanAnatAndMask(opt, funcFWHM, rfxDir);
+
+ % ------
+ % TODO
+ % needs to be improved (maybe??) as the structural and mask may vary for
+ % different analysis
+ % ------
+
+ saveMatlabBatch(matlabbatch, 'create_mean_struc_mask', opt);
+
+ spm_jobman('run', matlabbatch);
+
+ %% Factorial design specification
+
+ % Load the list of contrasts of interest for the RFX
+ grpLvlCon = getGrpLevelContrastToCompute(opt);
+
+ % ------
+ % TODO
+ % rfxDir should probably be set in setBatchFactorialDesign
+ % ------
+
+ rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
+
+ matlabbatch = setBatchFactorialDesign(grpLvlCon, group, conFWHM, rfxDir);
+
+ % ------
+ % TODO
+ % needs to be improved (maybe??) as the name may vary with FXHM and
+ % contrast
+ % ------
+
+ saveMatlabBatch(matlabbatch, 'rfx_specification', opt);
+
+ fprintf(1, 'Factorial Design Specification...');
+
+ spm_jobman('run', matlabbatch);
+
+ %% Factorial design estimation
+
+ fprintf(1, 'BUILDING JOB: Factorial Design Estimation');
+
+ matlabbatch = {};
+
+ for j = 1:size(grpLvlCon, 1)
+ conName = rmTrialTypeStr(grpLvlCon{j});
+ matlabbatch{j}.spm.stats.fmri_est.spmmat = ...
+ { fullfile(rfxDir, conName, 'SPM.mat') }; %#ok<*AGROW>
+ matlabbatch{j}.spm.stats.fmri_est.method.Classical = 1;
+ end
+
+ % ------
+ % TODO
+ % needs to be improved (maybe??) as the name may vary with FXHM and
+ % contrast
+ % ------
+
+ saveMatlabBatch(matlabbatch, 'rfx_estimation', opt);
+
+ fprintf(1, 'Factorial Design Estimation...');
+
+ spm_jobman('run', matlabbatch);
+
+ %% Contrast estimation
+
+ fprintf(1, 'BUILDING JOB: Contrast estimation');
+
+ matlabbatch = {};
+
+ % ADD/REMOVE CONTRASTS DEPENDING ON YOUR EXPERIMENT AND YOUR GROUPS
+ for j = 1:size(grpLvlCon, 1)
+ conName = rmTrialTypeStr(grpLvlCon{j});
+ matlabbatch{j}.spm.stats.con.spmmat = ...
+ {fullfile(rfxDir, conName, 'SPM.mat')};
+ matlabbatch{j}.spm.stats.con.consess{1}.tcon.name = 'GROUP';
+ matlabbatch{j}.spm.stats.con.consess{1}.tcon.convec = 1;
+ matlabbatch{j}.spm.stats.con.consess{1}.tcon.sessrep = 'none';
+
+ matlabbatch{j}.spm.stats.con.delete = 0;
+ end
+
+ % ------
+ % TODO
+ % needs to be improved (maybe??) as the name may vary with FXHM and
+ % contrast
+ % ------
+
+ saveMatlabBatch(matlabbatch, 'rfx_contrasts', opt);
+
+ fprintf(1, 'Contrast Estimation...');
+
+ spm_jobman('run', matlabbatch);
+
+ end
+
+end
+
+function conName = rmTrialTypeStr(conName)
+ conName = strrep(conName, 'trial_type.', '');
+end
diff --git a/src/workflows/bidsRealignReslice.m b/src/workflows/bidsRealignReslice.m
new file mode 100644
index 00000000..3b3c9b26
--- /dev/null
+++ b/src/workflows/bidsRealignReslice.m
@@ -0,0 +1,50 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function bidsRealignReslice(opt)
+ % bidsRealignReslice(opt)
+ %
+ % The scripts realigns the functional
+ % Assumes that bidsSTC has already been run
+
+ %% TO DO
+ % find a way to paralelize this over subjects
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % 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)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ % Get the ID of the subject
+ % (i.e SubNumber doesnt have to match the iSub if one subject
+ % is exluded for any reason)
+ subID = group(iGroup).subNumber{iSub}; % Get the subject ID
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ matlabbatch = [];
+ [matlabbatch, ~] = setBatchRealign( ...
+ matlabbatch, ...
+ BIDS, ...
+ subID, ...
+ opt, ...
+ 'realignReslice');
+
+ saveMatlabBatch(matlabbatch, 'realign_reslice', opt, subID);
+
+ spm_jobman('run', matlabbatch);
+
+ end
+ end
+
+end
diff --git a/src/workflows/bidsRealignUnwarp.m b/src/workflows/bidsRealignUnwarp.m
new file mode 100644
index 00000000..15da1897
--- /dev/null
+++ b/src/workflows/bidsRealignUnwarp.m
@@ -0,0 +1,50 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function bidsRealignUnwarp(opt)
+ % bidsRealignReslice(opt)
+ %
+ % The scripts realigns the functional
+ % Assumes that bidsSTC has already been run
+
+ %% TO DO
+ % find a way to paralelize this over subjects
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % 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)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ % Get the ID of the subject
+ % (i.e SubNumber doesnt have to match the iSub if one subject
+ % is exluded for any reason)
+ subID = group(iGroup).subNumber{iSub}; % Get the subject ID
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ matlabbatch = [];
+ [matlabbatch, ~] = setBatchRealign( ...
+ matlabbatch, ...
+ BIDS, ...
+ subID, ...
+ opt, ...
+ 'realignUnwarp');
+
+ saveMatlabBatch(matlabbatch, 'realign_unwarp', opt, subID);
+
+ spm_jobman('run', matlabbatch);
+
+ end
+ end
+
+end
diff --git a/src/workflows/bidsResliceTpmToFunc.m b/src/workflows/bidsResliceTpmToFunc.m
new file mode 100644
index 00000000..76dfe277
--- /dev/null
+++ b/src/workflows/bidsResliceTpmToFunc.m
@@ -0,0 +1,47 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function bidsResliceTpmToFunc(opt)
+ % bidsResliceTpmToFunc(opt)
+ %
+ % reslices the tissue probability map from the segmentation to the mean
+ % functional
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ [group, opt, BIDS] = getData(opt);
+
+ fprintf(1, 'RESLICING TPM TO MEAN FUNCTIONAL\n\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};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ [meanImage, meanFuncDir] = getMeanFuncFilename(BIDS, subID, opt);
+
+ % get grey and white matter and CSF tissue probability maps
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+ TPMs = validationInputFile(anatDataDir, anatImage, 'c[123]');
+
+ matlabbatch = setBatchReslice( ...
+ fullfile(meanFuncDir, meanImage), ...
+ cellstr(TPMs));
+
+ saveMatlabBatch(matlabbatch, 'reslice_tpm', opt, subID);
+ spm_jobman('run', matlabbatch);
+
+ end
+
+ end
+
+end
diff --git a/src/workflows/bidsResults.m b/src/workflows/bidsResults.m
new file mode 100644
index 00000000..1673fd4b
--- /dev/null
+++ b/src/workflows/bidsResults.m
@@ -0,0 +1,173 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function bidsResults(opt, funcFWHM, conFWHM)
+ % This scripts computes the results for a series of contrast that can be
+ % specified at the run, subject or dataset step level (see contrast specification
+ % following the BIDS stats model specification)
+ %
+ % funcFWHM is the number of the mm smoothing used on the normalized functional files.
+ % for unsmoothied data FFXSmoothing = 0
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % load the subjects/Groups information and the task name
+ [group, opt] = getData(opt);
+
+ matlabbatch = [];
+
+ % loop trough the steps and more results to compute for each contrast
+ % mentioned for each step
+ for iStep = 1:length(opt.result.Steps)
+
+ for iCon = 1:length(opt.result.Steps(iStep).Contrasts)
+
+ % Depending on the level step we migh have to define a matlabbatch
+ % for each subject or just on for the whole group
+ switch opt.result.Steps(iStep).Level
+
+ case 'run'
+ warning('run level not implemented yet');
+
+ % saveMatlabBatch(matlabbatch, 'computeFfxResults', opt, subID);
+
+ case 'subject'
+
+ matlabbatch = ...
+ setBatchSubjectLevelResults( ...
+ matlabbatch, ...
+ group, ...
+ funcFWHM, ...
+ opt, ...
+ iStep, ...
+ iCon);
+
+ % TODO
+ % Save this batch in for each subject and not once for all
+
+ saveMatlabBatch(matlabbatch, 'compute_ffx_results', opt);
+
+ case 'dataset'
+
+ rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
+
+ load(fullfile(rfxDir, 'SPM.mat'));
+
+ results.dir = rfxDir;
+ results.contrastNb = 1;
+ results.label = 'group level';
+ results.nbSubj = SPM.nscan;
+
+ matlabbatch = resultsMatlabbatch( ...
+ matlabbatch, opt, iStep, iCon, results);
+
+ saveMatlabBatch(matlabbatch, 'compute_rfx_results', opt);
+
+ end
+ end
+
+ end
+
+ if ~isempty(matlabbatch)
+
+ spm_jobman('run', matlabbatch);
+
+ % move ps file
+ % TODO
+
+ % rename NIDM file
+ % TODO
+ end
+
+end
+
+function batch = resultsMatlabbatch(batch, opt, iStep, iCon, results)
+ % outputs the typical matlabbatch to compute the results for a given
+ % contrast
+
+ batch{end + 1}.spm.stats.results.spmmat = {fullfile(results.dir, 'SPM.mat')};
+
+ batch{end}.spm.stats.results.conspec.titlestr = ...
+ opt.result.Steps(iStep).Contrasts(iCon).Name;
+
+ batch{end}.spm.stats.results.conspec.contrasts = results.contrastNb;
+
+ batch{end}.spm.stats.results.conspec.threshdesc = ...
+ opt.result.Steps(iStep).Contrasts(iCon).MC;
+
+ batch{end}.spm.stats.results.conspec.thresh = opt.result.Steps(iStep).Contrasts(iCon).p;
+
+ batch{end}.spm.stats.results.conspec.extent = opt.result.Steps(iStep).Contrasts(iCon).k;
+
+ batch{end}.spm.stats.results.conspec.conjunction = 1;
+
+ batch{end}.spm.stats.results.conspec.mask.none = ...
+ ~opt.result.Steps(iStep).Contrasts(iCon).Mask;
+
+ batch{end}.spm.stats.results.units = 1;
+
+ batch{end}.spm.stats.results.export{1}.ps = true;
+
+ if opt.result.Steps(1).Contrasts(iCon).NIDM
+
+ batch{end}.spm.stats.results.export{2}.nidm.modality = 'FMRI';
+
+ batch{end}.spm.stats.results.export{2}.nidm.refspace = 'ixi';
+ if strcmp(opt.space, 'T1w')
+ batch{end}.spm.stats.results.export{2}.nidm.refspace = 'subject';
+ end
+
+ batch{end}.spm.stats.results.export{2}.nidm.group.nsubj = results.nbSubj;
+
+ batch{end}.spm.stats.results.export{2}.nidm.group.label = results.label;
+
+ end
+
+end
+
+function batch = setBatchSubjectLevelResults(varargin)
+
+ [batch, grp, funcFWHM, opt, iStep, iCon] = deal(varargin{:});
+
+ for iGroup = 1:length(grp)
+
+ % For each subject
+ for iSub = 1:grp(iGroup).numSub
+
+ % Get the Subject ID
+ subID = grp(iGroup).subNumber{iSub};
+
+ % FFX Directory
+ ffxDir = getFFXdir(subID, funcFWHM, opt);
+
+ load(fullfile(ffxDir, 'SPM.mat'));
+
+ % identify which contrast nb actually has the name the user asked
+ conNb = find( ...
+ strcmp({SPM.xCon.name}', ...
+ opt.result.Steps(iStep).Contrasts(iCon).Name));
+
+ if isempty(conNb)
+ sprintf('List of contrast in this SPM file');
+ disp({SPM.xCon.name}');
+ error( ...
+ 'This SPM file %s does not contain a contrast named %s', ...
+ fullfile(ffxDir, 'SPM.mat'), ...
+ opt.result.Steps(1).Contrasts(iCon).Name);
+ end
+
+ results.dir = ffxDir;
+ results.contrastNb = conNb;
+ results.label = subID;
+ results.nbSubj = 1;
+
+ batch = resultsMatlabbatch( ...
+ batch, opt, iStep, iCon, results);
+
+ end
+ end
+
+end
diff --git a/src/workflows/bidsSTC.m b/src/workflows/bidsSTC.m
new file mode 100644
index 00000000..49e2334c
--- /dev/null
+++ b/src/workflows/bidsSTC.m
@@ -0,0 +1,62 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function bidsSTC(opt)
+ % Performs SLICE TIMING CORRECTION of the functional data. The
+ % script for each subject and can handle multiple sessions and multiple
+ % runs.
+ %
+ % Slice timing units is in milliseconds to be BIDS compliant and not in slice number
+ % as is more traditionally the case with SPM.
+ %
+ % In the case the slice timing information was not specified in the json FILES
+ % in the BIDS data set (e.g it couldnt be extracted from the trento old scanner),
+ % then add this information manually in opt.sliceOrder field.
+ %
+ % If this is empty the slice timing correction will not be performed
+ %
+ % If not specified this function will take the mid-volume time point as reference
+ % to do the slice timing correction
+ %
+ % See README.md for more information about slice timing correction
+ %
+ % INPUT:
+ % 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
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % load the subjects/Groups information and the task name
+ [group, opt, BIDS] = getData(opt);
+
+ fprintf(1, 'DOING SLICE TIME CORRECTION\n');
+
+ %% Loop through the groups, subjects, and sessions
+ for iGroup = 1:length(group)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ % Get the ID of the subject
+ % (i.e SubNumber doesnt have to match the iSub if one subject is exluded)
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ matlabbatch = setBatchSTC(BIDS, opt, subID);
+
+ if ~isempty(matlabbatch)
+ saveMatlabBatch(matlabbatch, 'STC', opt, subID);
+
+ spm_jobman('run', matlabbatch);
+ end
+
+ end
+ end
+
+end
diff --git a/src/workflows/bidsSegmentSkullStrip.m b/src/workflows/bidsSegmentSkullStrip.m
new file mode 100644
index 00000000..d232174f
--- /dev/null
+++ b/src/workflows/bidsSegmentSkullStrip.m
@@ -0,0 +1,56 @@
+% (C) Copyright 2020 CPP BIDS SPM-pipeline developers
+
+function bidsSegmentSkullStrip(opt)
+ %
+ % Segments and skullstrips the anat image.
+ %
+ % USAGE::
+ %
+ % bidsSegmentSkullStrip([opt])
+ %
+ % :param opt: options
+ % :type opt: structure
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % load the subjects/Groups information and the task name
+ [group, opt, BIDS] = getData(opt);
+
+ fprintf(1, 'SEGMENTING AND SKULL STRIPPING ANAT\n');
+
+ %% Loop through the groups, subjects, and sessions
+ for iGroup = 1:length(group)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ matlabbatch = [];
+ % Get the ID of the subject
+ % (i.e SubNumber doesnt have to match the iSub if one subject
+ % is exluded for any reason)
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subID);
+ opt.orderBatches.selectAnat = 1;
+
+ % dependency from file selector ('Anatomical')
+ matlabbatch = setBatchSegmentation(matlabbatch, opt);
+ opt.orderBatches.segment = 2;
+
+ matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, subID, opt);
+
+ saveMatlabBatch(matlabbatch, 'segment_skullstrip', opt, subID);
+
+ spm_jobman('run', matlabbatch);
+
+ end
+ end
+
+end
diff --git a/src/workflows/bidsSmoothing.m b/src/workflows/bidsSmoothing.m
new file mode 100644
index 00000000..c98c0c24
--- /dev/null
+++ b/src/workflows/bidsSmoothing.m
@@ -0,0 +1,36 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function bidsSmoothing(funcFWHM, opt)
+ % This scripts performs smoothing to the functional data using a full width
+ % half maximum smoothing kernel of size "mm_smoothing".
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 2
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ % 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)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ matlabbatch = setBatchSmoothing(BIDS, opt, subID, funcFWHM);
+
+ saveMatlabBatch(matlabbatch, ['smoothing_FWHM-' num2str(funcFWHM)], opt, subID);
+
+ spm_jobman('run', matlabbatch);
+
+ end
+ end
+
+end
diff --git a/src/workflows/bidsSpatialPrepro.m b/src/workflows/bidsSpatialPrepro.m
new file mode 100644
index 00000000..0616e0f9
--- /dev/null
+++ b/src/workflows/bidsSpatialPrepro.m
@@ -0,0 +1,90 @@
+% (C) Copyright 2019 CPP BIDS SPM-pipeline developers
+
+function bidsSpatialPrepro(opt)
+ % bidsSpatialPrepro(opt)
+ %
+ % Performs spatial preprocessing of the functional and structural data.
+ %
+ % TODO update description
+ % The structural data are segmented and normalized to MNI space.
+ % The functional data are re-aligned, coregistered with the structural and
+ % normalized to MNI space.
+ %
+ % Assumptions:
+ % - the batch is build using dependencies across the different batch modules
+
+ % TO DO
+ % - find a way to paralelize this over subjects
+ % - average T1s across sessions if necessarry
+
+ % if input has no opt, load the opt.mat file
+ if nargin < 1
+ opt = [];
+ end
+ opt = loadAndCheckOptions(opt);
+
+ setGraphicWindow();
+
+ % load the subjects/Groups information and the task name
+ [group, opt, BIDS] = getData(opt);
+
+ fprintf(1, 'DOING SPATIAL PREPROCESSING\n');
+
+ %% Loop through the groups, subjects, and sessions
+ for iGroup = 1:length(group)
+
+ groupName = group(iGroup).name;
+
+ for iSub = 1:group(iGroup).numSub
+
+ matlabbatch = [];
+ % Get the ID of the subject
+ % (i.e SubNumber doesnt have to match the iSub if one subject
+ % is exluded for any reason)
+ subID = group(iGroup).subNumber{iSub};
+
+ printProcessingSubject(groupName, iSub, subID);
+
+ matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subID);
+ opt.orderBatches.selectAnat = 1;
+
+ action = [];
+ if strcmp(opt.space, 'individual')
+ action = 'realignUnwarp';
+ end
+ [matlabbatch, voxDim] = setBatchRealign(matlabbatch, BIDS, subID, opt, action);
+ opt.orderBatches.realign = 2;
+
+ % dependency from file selector ('Anatomical')
+ matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt);
+ opt.orderBatches.coregister = 3;
+
+ matlabbatch = setBatchSaveCoregistrationMatrix(matlabbatch, BIDS, subID, opt);
+
+ % dependency from file selector ('Anatomical')
+ matlabbatch = setBatchSegmentation(matlabbatch, opt);
+ opt.orderBatches.segment = 5;
+
+ matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, subID, opt);
+
+ if strcmp(opt.space, 'MNI')
+ % dependency from segmentation
+ % dependency from coregistration
+ matlabbatch = setBatchNormalizationSpatialPrepro(matlabbatch, voxDim, opt);
+ end
+
+ batchName = ['spatial_preprocessing-' upper(opt.space(1)) opt.space(2:end)];
+ saveMatlabBatch(matlabbatch, batchName, opt, subID);
+
+ spm_jobman('run', matlabbatch);
+
+ imgNb = copyGraphWindownOutput(opt, subID, 'realign');
+ if strcmp(opt.space, 'individual')
+ imgNb = copyGraphWindownOutput(opt, subID, 'unwarp', imgNb);
+ end
+ imgNb = copyGraphWindownOutput(opt, subID, 'func2anatCoreg', imgNb);
+
+ end
+ end
+
+end
diff --git a/tests/README.md b/tests/README.md
index 603db339..28db08aa 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,8 +1,10 @@
# README
+```matlab
coverage = mocov( ...
'-expression', 'moxunit_runtests()', ...
'-verbose', ...
'-cover', fullfile(pwd, '..', 'subfun'), ....
'-cover_xml_file', 'coverage.xml', ...
- '-cover_html_dir', 'coverage_html')
\ No newline at end of file
+ '-cover_html_dir', 'coverage_html')
+```
diff --git a/tests/createDummyDataSet.sh b/tests/createDummyDataSet.sh
index 7e3e8df2..7f72ae92 100755
--- a/tests/createDummyDataSet.sh
+++ b/tests/createDummyDataSet.sh
@@ -7,7 +7,7 @@ StartDir=`pwd` # relative to starting directory
StartDir=$StartDir/dummyData/derivatives/SPM12_CPPL
mkdir $StartDir
-SubList='ctrl01 ctrl02 blind01 blind02 cat01 cat02 cont01 cont02 cont03 01 02' # subject list
+SubList='ctrl01 ctrl02 blind01 blind02 01 02' # subject list
SesList='01 02' # session list
for Subject in $SubList # loop through subjects
@@ -26,11 +26,17 @@ do
rm $ThisDir/func/*
- touch $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vismotion_run-1_bold.nii.gz
- touch $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vismotion_run-2_bold.nii.gz
- touch $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii.gz
+ touch $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vismotion_run-1_bold.nii
+ touch $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vismotion_run-2_bold.nii
+ touch $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii
+
+ touch $ThisDir/func/asub-$Subject\_ses-$Ses\_task-vismotion_run-1_bold.nii
+ touch $ThisDir/func/asub-$Subject\_ses-$Ses\_task-vismotion_run-2_bold.nii
+
+ touch $ThisDir/func/meanusub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii
touch $ThisDir/func/s6wsub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii
touch $ThisDir/func/s6rsub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii
+ touch $ThisDir/func/s6usub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii
touch $ThisDir/func/rp_sub-$Subject\_ses-$Ses\_task-vislocalizer_bold.txt
echo "onset\tduration\ttrial_type" >> $ThisDir/func/sub-$Subject\_ses-$Ses\_task-vislocalizer_events.tsv
@@ -47,7 +53,7 @@ do
mkdir $ThisDir/anat
- touch $ThisDir/anat/sub-$Subject\_ses-$Ses\_T1w.nii.gz
+ touch $ThisDir/anat/sub-$Subject\_ses-$Ses\_T1w.nii
done
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/participants.tsv b/tests/dummyData/derivatives/SPM12_CPPL/participants.tsv
index b601658d..0672bef8 100755
--- a/tests/dummyData/derivatives/SPM12_CPPL/participants.tsv
+++ b/tests/dummyData/derivatives/SPM12_CPPL/participants.tsv
@@ -1,9 +1,6 @@
participant_id Sex Age Educational level Smoker Medication Handedness
-sub-blnd01 1 66 14 0 1 30
-sub-blnd02 1 28 16 1 0 12
+sub-01 1 66 14 0 1 30
+sub-02 1 28 16 1 0 12
sub-ctrl01 0 61 16 0 0 18
-sub-ctrl02 1 29 12 0 0 11
-sub-cat01 0 24 12 0 0 23
-sub-cat02 1 56 11 0 1 11
-sub-cont01 1 32 5 1 0 30
-sub-cont02 0 43 3 1 1 20
+
+
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/anat/sub-01_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/anat/sub-01_ses-01_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/anat/sub-01_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/anat/sub-01_ses-01_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/asub-01_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/asub-01_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/asub-01_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/asub-01_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/meanusub-01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/meanusub-01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/anat/sub-01_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/s6usub-01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/anat/sub-01_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/s6usub-01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-01/func/sub-01_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/anat/sub-02_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/anat/sub-01_ses-02_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/anat/sub-02_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/anat/sub-01_ses-02_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/asub-01_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/asub-01_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/asub-01_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/asub-01_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/meanusub-01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/meanusub-01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/anat/sub-02_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/s6usub-01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/anat/sub-02_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/s6usub-01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/ses-02/func/sub-01_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-01/stats/ffx_visMotion/ffx_6/SPM.mat b/tests/dummyData/derivatives/SPM12_CPPL/sub-01/stats/ffx_visMotion/ffx_space-MNI_FWHM-6/SPM.mat
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-01/stats/ffx_visMotion/ffx_6/SPM.mat
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-01/stats/ffx_visMotion/ffx_space-MNI_FWHM-6/SPM.mat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/anat/sub-blind01_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/anat/sub-02_ses-01_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/anat/sub-blind01_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/anat/sub-02_ses-01_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/asub-02_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/asub-02_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/asub-02_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/asub-02_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/meanusub-02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/meanusub-02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/anat/sub-blind01_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/s6usub-02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/anat/sub-blind01_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/s6usub-02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-01/func/sub-02_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/anat/sub-blind02_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/anat/sub-02_ses-02_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/anat/sub-blind02_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/anat/sub-02_ses-02_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/asub-02_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/asub-02_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/asub-02_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/asub-02_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/meanusub-02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/meanusub-02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/anat/sub-blind02_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/s6usub-02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/anat/sub-blind02_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/s6usub-02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-02/ses-02/func/sub-02_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/anat/sub-cat01_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/anat/sub-blind01_ses-01_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/anat/sub-cat01_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/anat/sub-blind01_ses-01_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/rp_sub-cat01_ses-01_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/asub-blind01_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/rp_sub-cat01_ses-01_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/asub-blind01_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/s6rsub-cat01_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/asub-blind01_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/s6rsub-cat01_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/asub-blind01_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/s6wsub-cat01_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/meanusub-blind01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/s6wsub-cat01_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/meanusub-blind01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/s6usub-blind01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/s6usub-blind01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/anat/sub-cat01_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/anat/sub-cat01_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-01/func/sub-blind01_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/rp_sub-cat01_ses-02_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/anat/sub-blind01_ses-02_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/rp_sub-cat01_ses-02_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/anat/sub-blind01_ses-02_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/s6rsub-cat01_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/asub-blind01_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/s6rsub-cat01_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/asub-blind01_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/s6wsub-cat01_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/asub-blind01_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/s6wsub-cat01_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/asub-blind01_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/meanusub-blind01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/meanusub-blind01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/s6usub-blind01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/s6usub-blind01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/anat/sub-cat02_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/anat/sub-cat02_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/rp_sub-cat02_ses-01_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/rp_sub-cat02_ses-01_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind01/ses-02/func/sub-blind01_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/s6rsub-cat02_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/anat/sub-blind02_ses-01_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/s6rsub-cat02_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/anat/sub-blind02_ses-01_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/s6wsub-cat02_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/asub-blind02_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/s6wsub-cat02_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/asub-blind02_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/asub-blind02_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/asub-blind02_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/meanusub-blind02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/meanusub-blind02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/s6usub-blind02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/s6usub-blind02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/anat/sub-cat02_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/anat/sub-cat02_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/rp_sub-cat02_ses-02_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/rp_sub-cat02_ses-02_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/s6rsub-cat02_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/s6rsub-cat02_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-01/func/sub-blind02_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/s6wsub-cat02_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/anat/sub-blind02_ses-02_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/s6wsub-cat02_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/anat/sub-blind02_ses-02_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/asub-blind02_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/asub-blind02_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/asub-blind02_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/asub-blind02_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/meanusub-blind02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/meanusub-blind02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/anat/sub-cont01_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/s6usub-blind02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/anat/sub-cont01_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/s6usub-blind02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/rp_sub-cont01_ses-01_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/rp_sub-cont01_ses-01_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/s6rsub-cont01_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/s6rsub-cont01_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/s6wsub-cont01_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/s6wsub-cont01_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-blind02/ses-02/func/sub-blind02_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-01/func/sub-cat01_ses-01_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat01/ses-02/func/sub-cat01_ses-02_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-01/func/sub-cat02_ses-01_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cat02/ses-02/func/sub-cat02_ses-02_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/rp_sub-cont03_ses-02_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/rp_sub-cont03_ses-02_task-vislocalizer_bold.txt
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/s6rsub-cont03_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/s6rsub-cont03_ses-02_task-vislocalizer_bold.nii
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/s6wsub-cont03_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/s6wsub-cont03_ses-02_task-vislocalizer_bold.nii
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vislocalizer_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vislocalizer_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vislocalizer_events.tsv
deleted file mode 100644
index eef890c1..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vislocalizer_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 15 VisMot
-25 15 VisStat
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-1_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-1_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-1_events.tsv
deleted file mode 100644
index 899cce33..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-1_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-2 2 VisMotUp
-4 2 VisMotDown
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-2_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-2_events.tsv b/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-2_events.tsv
deleted file mode 100644
index 4b01c47d..00000000
--- a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/func/sub-cont03_ses-02_task-vismotion_run-2_events.tsv
+++ /dev/null
@@ -1,3 +0,0 @@
-onset duration trial_type
-3 2 VisMotDown
-6 2 VisMotUp
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/anat/sub-ctrl01_ses-01_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/anat/sub-ctrl01_ses-01_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/anat/sub-ctrl01_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/anat/sub-ctrl01_ses-01_T1w.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/asub-ctrl01_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/asub-ctrl01_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/asub-ctrl01_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-01/func/sub-cont01_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/asub-ctrl01_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/anat/sub-cont01_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/meanusub-ctrl01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/anat/sub-cont01_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/meanusub-ctrl01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/rp_sub-cont01_ses-02_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/s6usub-ctrl01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/rp_sub-cont01_ses-02_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/s6usub-ctrl01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/s6rsub-cont01_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/s6rsub-cont01_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vislocalizer_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/s6wsub-cont01_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/s6wsub-cont01_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-1_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-01/func/sub-ctrl01_ses-01_task-vismotion_run-2_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/anat/sub-ctrl01_ses-02_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/anat/sub-ctrl01_ses-02_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/anat/sub-ctrl01_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/anat/sub-ctrl01_ses-02_T1w.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/asub-ctrl01_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont01/ses-02/func/sub-cont01_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/asub-ctrl01_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/anat/sub-cont02_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/asub-ctrl01_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/anat/sub-cont02_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/asub-ctrl01_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/rp_sub-cont02_ses-01_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/meanusub-ctrl01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/rp_sub-cont02_ses-01_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/meanusub-ctrl01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/s6rsub-cont02_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/s6usub-ctrl01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/s6rsub-cont02_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/s6usub-ctrl01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/s6wsub-cont02_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/s6wsub-cont02_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vislocalizer_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-1_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl01/ses-02/func/sub-ctrl01_ses-02_task-vismotion_run-2_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/anat/sub-ctrl02_ses-01_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-01/func/sub-cont02_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/anat/sub-ctrl02_ses-01_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/anat/sub-ctrl02_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/anat/sub-ctrl02_ses-01_T1w.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/anat/sub-cont02_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/asub-ctrl02_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/anat/sub-cont02_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/asub-ctrl02_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/rp_sub-cont02_ses-02_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/asub-ctrl02_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/rp_sub-cont02_ses-02_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/asub-ctrl02_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/s6rsub-cont02_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/meanusub-ctrl02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/s6rsub-cont02_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/meanusub-ctrl02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/s6wsub-cont02_ses-02_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/s6usub-ctrl02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/s6wsub-cont02_ses-02_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/s6usub-ctrl02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vislocalizer_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-1_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont02/ses-02/func/sub-cont02_ses-02_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-01/func/sub-ctrl02_ses-01_task-vismotion_run-2_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/anat/sub-cont03_ses-01_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/anat/sub-ctrl02_ses-02_T1w.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/anat/sub-cont03_ses-01_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/anat/sub-ctrl02_ses-02_T1w.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/anat/sub-ctrl02_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/anat/sub-ctrl02_ses-02_T1w.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/rp_sub-cont03_ses-01_task-vislocalizer_bold.txt b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/asub-ctrl02_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/rp_sub-cont03_ses-01_task-vislocalizer_bold.txt
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/asub-ctrl02_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/s6rsub-cont03_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/asub-ctrl02_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/s6rsub-cont03_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/asub-ctrl02_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/s6wsub-cont03_ses-01_task-vislocalizer_bold.nii b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/meanusub-ctrl02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/s6wsub-cont03_ses-01_task-vislocalizer_bold.nii
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/meanusub-ctrl02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/s6usub-ctrl02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vislocalizer_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/s6usub-ctrl02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vislocalizer_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-1_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vislocalizer_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vislocalizer_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vislocalizer_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-1_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-01/func/sub-cont03_ses-01_task-vismotion_run-2_bold.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-1_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-1_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-1_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/anat/sub-cont03_ses-02_T1w.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-2_bold.nii
similarity index 100%
rename from tests/dummyData/derivatives/SPM12_CPPL/sub-cont03/ses-02/anat/sub-cont03_ses-02_T1w.nii.gz
rename to tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-2_bold.nii
diff --git a/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-2_bold.nii.gz b/tests/dummyData/derivatives/SPM12_CPPL/sub-ctrl02/ses-02/func/sub-ctrl02_ses-02_task-vismotion_run-2_bold.nii.gz
deleted file mode 100644
index e69de29b..00000000
diff --git a/model-visMotionLoc_smdl.json b/tests/dummyData/models/model-visMotionLoc_smdl.json
similarity index 100%
rename from model-visMotionLoc_smdl.json
rename to tests/dummyData/models/model-visMotionLoc_smdl.json
diff --git a/tests/dummyData/model/model-vislocalizer_smdl.json b/tests/dummyData/models/model-vislocalizer_smdl.json
similarity index 100%
rename from tests/dummyData/model/model-vislocalizer_smdl.json
rename to tests/dummyData/models/model-vislocalizer_smdl.json
diff --git a/tests/dummyData/raw/README.md b/tests/dummyData/raw/README.md
index 70a0110b..49892820 100644
--- a/tests/dummyData/raw/README.md
+++ b/tests/dummyData/raw/README.md
@@ -1,3 +1,3 @@
# README
-placeholder
\ No newline at end of file
+placeholder
diff --git a/tests/test_bidsCopyRawFolder.m b/tests/test_bidsCopyRawFolder.m
new file mode 100644
index 00000000..fa559eea
--- /dev/null
+++ b/tests/test_bidsCopyRawFolder.m
@@ -0,0 +1,44 @@
+function test_suite = test_bidsCopyRawFolder %#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
+ end
+ initTestSuite;
+end
+
+function test_bidsCopyRawFolderBasic()
+
+ % TODO add test to only copy some modalities
+
+ % directory with this script becomes the current directory
+ opt.dataDir = fullfile( ...
+ fileparts(mfilename('fullpath')), ...
+ '..', 'demos', 'MoAE', 'output', 'MoAEpilot');
+
+ % task to analyze
+ opt.taskName = 'auditory';
+
+ opt = checkOptions(opt);
+
+ checkDependencies();
+
+ bidsCopyRawFolder(opt, 1);
+
+ assertEqual(exist( ...
+ fullfile(opt.dataDir, '..', ...
+ 'derivatives', 'SPM12_CPPL', ...
+ 'dataset_description.json'), 'file'), ...
+ 2);
+
+ assertEqual(exist( ...
+ fullfile(opt.dataDir, '..', ...
+ 'derivatives', 'SPM12_CPPL', 'sub-01', 'func', ...
+ 'sub-01_task-auditory_bold.nii'), 'file'), ...
+ 2);
+
+ assertEqual(exist( ...
+ fullfile(opt.dataDir, '..', ...
+ 'derivatives', 'SPM12_CPPL', 'sub-01', 'anat', ...
+ 'sub-01_T1w.nii'), 'file'), ...
+ 2);
+end
diff --git a/tests/test_checkOptions.m b/tests/test_checkOptions.m
index 46f5e45b..232f1684 100644
--- a/tests/test_checkOptions.m
+++ b/tests/test_checkOptions.m
@@ -1,79 +1,118 @@
function test_suite = test_checkOptions %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_checkOptionsBasic()
- opt = checkOptions();
+ opt.taskName = 'testTask';
+ opt = checkOptions(opt);
- expectedOptions = defaultOptions();
+ expectedOptions = defaultOptions();
+ expectedOptions.taskName = 'testTask';
- assertEqual(opt, expectedOptions);
+ assertEqual(opt, expectedOptions);
end
function test_checkOptionsDoNotOverwrite()
- opt.funcVoxelDims = [1 1 1];
- opt.someExtraField = 'test';
+ opt.funcVoxelDims = [1 1 1];
+ opt.someExtraField = 'test';
+ opt.taskName = 'testTask';
- opt = checkOptions(opt);
+ opt = checkOptions(opt);
- assertEqual(opt.funcVoxelDims, [1 1 1]);
- assertEqual(opt.someExtraField, 'test');
+ assertEqual(opt.funcVoxelDims, [1 1 1]);
+ assertEqual(opt.someExtraField, 'test');
+
+end
+
+function test_checkOptionsErrorTask()
+
+ opt.taskName = [];
+
+ assertExceptionThrown( ...
+ @()checkOptions(opt), ...
+ 'checkOptions:noTask');
end
function test_checkOptionsErrorGroup()
- opt.groups = {1};
+ opt.groups = {1};
+ opt.taskName = 'testTask';
- assertExceptionThrown( ...
- @()checkOptions(opt), ...
- 'checkOptions:groupNotString');
+ assertExceptionThrown( ...
+ @()checkOptions(opt), ...
+ 'checkOptions:groupNotString');
end
function test_checkOptionsErrorRefSlice()
- opt.STC_referenceSlice = [1:10];
+ opt.STC_referenceSlice = [1:10];
+ opt.taskName = 'testTask';
- assertExceptionThrown( ...
- @()checkOptions(opt), ...
- 'checkOptions:refSliceNotScalar');
+ assertExceptionThrown( ...
+ @()checkOptions(opt), ...
+ 'checkOptions:refSliceNotScalar');
end
function test_checkOptionsErrorVoxDim()
- opt.funcVoxelDims = [1:10];
+ opt.funcVoxelDims = [1:10];
+ opt.taskName = 'testTask';
- assertExceptionThrown( ...
- @()checkOptions(opt), ...
- 'checkOptions:voxDim');
+ assertExceptionThrown( ...
+ @()checkOptions(opt), ...
+ 'checkOptions:voxDim');
end
function expectedOptions = defaultOptions()
- expectedOptions.STC_referenceSlice = [];
- expectedOptions.contrastList = {};
- expectedOptions.dataDir = '';
- expectedOptions.funcVoxelDims = [];
- expectedOptions.groups = {''};
- expectedOptions.jobsDir = '';
- expectedOptions.sliceOrder = [];
- expectedOptions.space = 'MNI';
- expectedOptions.subjects = {[]};
- expectedOptions.taskName = '';
- expectedOptions.zeropad = 2;
- expectedOptions.model = struct('file', '');
- expectedOptions.result = struct('Steps', '');
-
- expectedOptions = orderfields(expectedOptions);
+ expectedOptions.sliceOrder = [];
+ expectedOptions.STC_referenceSlice = [];
+
+ expectedOptions.dataDir = '';
+ expectedOptions.derivativesDir = '';
+
+ expectedOptions.funcVoxelDims = [];
+
+ expectedOptions.groups = {''};
+ expectedOptions.subjects = {[]};
+
+ expectedOptions.space = 'MNI';
+
+ expectedOptions.anatReference.type = 'T1w';
+ expectedOptions.anatReference.session = 1;
+
+ expectedOptions.skullstrip.threshold = 0.75;
+
+ expectedOptions.ignoreFieldmaps = false;
+
+ expectedOptions.taskName = '';
+
+ expectedOptions.zeropad = 2;
+
+ expectedOptions.contrastList = {};
+ expectedOptions.model.file = '';
+
+ expectedOptions.result.Steps = struct( ...
+ 'Level', '', ...
+ 'Contrasts', struct( ...
+ 'Name', '', ...
+ 'Mask', false, ...
+ 'MC', 'FWE', ...
+ 'p', 0.05, ...
+ 'k', 0, ...
+ 'NIDM', true));
+
+ expectedOptions = orderfields(expectedOptions);
end
diff --git a/tests/test_createAndReturnOnsetFile.m b/tests/test_createAndReturnOnsetFile.m
index 5832e07f..cba56757 100644
--- a/tests/test_createAndReturnOnsetFile.m
+++ b/tests/test_createAndReturnOnsetFile.m
@@ -1,39 +1,42 @@
function test_suite = test_createAndReturnOnsetFile %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_createAndReturnOnsetFileBasic()
- isMVPA = false;
- subID = '01';
- funcFWHM = 6;
- iSes = 1;
- iRun = 1;
+ subID = '01';
+ funcFWHM = 6;
+ iSes = 1;
+ iRun = 1;
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
- opt.model.univariate.file = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'model', ...
- 'model-vislocalizer_smdl.json');
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.subjects = {'01'};
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'models', ...
+ 'model-vislocalizer_smdl.json');
- [~, opt, BIDS] = getData(opt);
+ opt = checkOptions(opt);
- boldFileName = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
+ [~, opt, BIDS] = getData(opt);
- onsetFileName = createAndReturnOnsetFile(opt, subID, funcFWHM, boldFileName, isMVPA);
+ sessions = getInfo(BIDS, subID, opt, 'sessions');
+ runs = getInfo(BIDS, subID, opt, 'runs', sessions{iSes});
- expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'stats', ...
- 'ffx_task-vislocalizer', 'ffx_FWHM-6', ...
- 'onsets_sub-01_ses-01_task-vislocalizer_events.mat');
+ tsvFile = getInfo(BIDS, subID, opt, 'filename', sessions{iSes}, runs{iRun}, 'events');
- assertEqual(exist(onsetFileName, 'file'), 2);
- assertEqual(exist(expectedFileName, 'file'), 2);
+ onsetFileName = createAndReturnOnsetFile(opt, subID, tsvFile, funcFWHM);
+
+ expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'stats', ...
+ 'ffx_task-vislocalizer', 'ffx_space-MNI_FWHM-6', ...
+ 'onsets_sub-01_ses-01_task-vislocalizer_events.mat');
+
+ assertEqual(exist(onsetFileName, 'file'), 2);
+ assertEqual(exist(expectedFileName, 'file'), 2);
end
diff --git a/tests/test_createDataDictionary.m b/tests/test_createDataDictionary.m
new file mode 100644
index 00000000..1ee7ceb5
--- /dev/null
+++ b/tests/test_createDataDictionary.m
@@ -0,0 +1,44 @@
+function test_suite = test_createDataDictionary %#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
+ end
+ initTestSuite;
+end
+
+function test_createDataDictionaryBasic()
+
+ subID = '01';
+ iSes = 1;
+ iRun = 1;
+
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.groups = {''};
+ opt.subjects = {'01'};
+
+ [~, opt, BIDS] = getData(opt);
+
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
+
+ runs = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{iSes}, runs{iRun}, opt);
+
+ createDataDictionary(subFuncDataDir, fileName, 3);
+
+ expectedFileName = fullfile( ...
+ subFuncDataDir, ...
+ 'sub-01_ses-01_task-vislocalizer_desc-confounds_regressors.json');
+
+ content = spm_jsonread(expectedFileName);
+
+ expectedNbColumns = 27;
+ expectedHeaderCol = 'censoring_regressor_3';
+
+ assertEqual(numel(content.Columns), expectedNbColumns);
+ assertEqual(content.Columns{expectedNbColumns}, 'censoring_regressor_3');
+
+end
diff --git a/tests/test_getAnatFilename.m b/tests/test_getAnatFilename.m
new file mode 100644
index 00000000..2433b98a
--- /dev/null
+++ b/tests/test_getAnatFilename.m
@@ -0,0 +1,33 @@
+function test_suite = test_getAnatFilename %#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
+ end
+ initTestSuite;
+end
+
+function test_getAnatFilenameBasic()
+
+ subID = '01';
+
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.groups = {''};
+ opt.subjects = {subID};
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt);
+
+ expectedFileName = 'sub-01_ses-01_T1w.nii';
+
+ expectedAnatDataDir = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', ...
+ 'sub-01', 'ses-01', 'anat');
+
+ assertEqual(anatDataDir, expectedAnatDataDir);
+ assertEqual(anatImage, expectedFileName);
+
+end
diff --git a/tests/test_getBoldFilename.m b/tests/test_getBoldFilename.m
index 29d93219..60f9d6c1 100644
--- a/tests/test_getBoldFilename.m
+++ b/tests/test_getBoldFilename.m
@@ -1,39 +1,40 @@
function test_suite = test_getBoldFilename %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getBoldFilenameBasic()
- subID = '01';
- funcFWHM = 6;
- iSes = 1;
- iRun = 1;
+ subID = '01';
+ funcFWHM = 6;
+ iSes = 1;
+ iRun = 1;
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.groups = {''};
+ opt.subjects = {'01'};
- [~, opt, BIDS] = getData(opt);
+ [~, opt, BIDS] = getData(opt);
- sessions = getInfo(BIDS, subID, opt, 'Sessions');
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
- runs = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+ runs = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
- [fileName, subFuncDataDir] = getBoldFilename( ...
- BIDS, ...
- subID, sessions{iSes}, runs{iRun}, opt);
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{iSes}, runs{iRun}, opt);
- expectedFileName = 'sub-01_ses-01_task-vislocalizer_bold.nii';
+ expectedFileName = 'sub-01_ses-01_task-vislocalizer_bold.nii';
- expectedSubFuncDataDir = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'ses-01', 'func');
+ expectedSubFuncDataDir = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', ...
+ 'sub-01', 'ses-01', 'func');
- assertEqual(expectedSubFuncDataDir, subFuncDataDir);
- assertEqual(expectedFileName, fileName);
+ assertEqual(expectedSubFuncDataDir, subFuncDataDir);
+ assertEqual(expectedFileName, fileName);
end
diff --git a/tests/test_getBoldFilenameForFFX.m b/tests/test_getBoldFilenameForFFX.m
index 2ef048f9..f78ec52d 100644
--- a/tests/test_getBoldFilenameForFFX.m
+++ b/tests/test_getBoldFilenameForFFX.m
@@ -1,58 +1,62 @@
function test_suite = test_getBoldFilenameForFFX %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getBoldFilenameForFFXBasic()
- subID = '01';
- funcFWHM = 6;
- iSes = 1;
- iRun = 1;
+ subID = '01';
+ funcFWHM = 6;
+ iSes = 1;
+ iRun = 1;
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.subjects = {'01'};
- [~, opt, BIDS] = getData(opt);
+ opt = checkOptions(opt);
- [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
+ [~, opt, BIDS] = getData(opt);
- expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'ses-01', 'func', ...
- 's6wsub-01_ses-01_task-vislocalizer_bold.nii');
+ [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
- assertEqual('s6w', prefix);
- assertEqual({expectedFileName}, boldFileName);
+ expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', ...
+ 'ses-01', 'func', ...
+ 's6wsub-01_ses-01_task-vislocalizer_bold.nii');
+
+ assertEqual('s6w', prefix);
+ assertEqual(expectedFileName, boldFileName);
end
function test_getBoldFilenameForFFXNativeSpace()
- subID = '01';
- funcFWHM = 6;
- iSes = 1;
- iRun = 1;
+ subID = '01';
+ funcFWHM = 6;
+ iSes = 1;
+ iRun = 1;
+
+ opt.taskName = 'vislocalizer';
+ opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
+ opt.subjects = {'01'};
+ opt.space = 'individual';
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
- opt.space = 'T1w';
+ opt = checkOptions(opt);
- [~, opt, BIDS] = getData(opt);
+ [~, opt, BIDS] = getData(opt);
- [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
+ [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
- expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'ses-01', 'func', ...
- 's6rsub-01_ses-01_task-vislocalizer_bold.nii');
+ expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', ...
+ 'ses-01', 'func', ...
+ 's6usub-01_ses-01_task-vislocalizer_bold.nii');
- assertEqual('s6r', prefix);
- assertEqual({expectedFileName}, boldFileName);
+ assertEqual('s6u', prefix);
+ assertEqual(expectedFileName, boldFileName);
end
diff --git a/tests/test_getData.m b/tests/test_getData.m
index fc508ed8..7f4a07f4 100644
--- a/tests/test_getData.m
+++ b/tests/test_getData.m
@@ -1,77 +1,105 @@
function test_suite = test_getData %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getDataBasic()
- % Small test to ensure that getData returns what we asked for
+ % Small test to ensure that getData returns what we asked for
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.taskName = 'vismotion';
- opt.zeropad = 2;
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+ opt.zeropad = 2;
- %% Get all groups all subjects
- opt.groups = {''};
- opt.subjects = {[]};
+ %% Get all groups all subjects
+ opt.groups = {''};
+ opt.subjects = {[]};
- [group] = getData(opt);
+ [group] = getData(opt);
- assert(isequal(group(1).name, ''));
- assert(isequal(group.numSub, 11));
- assert(isequal(group.subNumber, ...
- { ...
- '01' '02' ...
- 'blind01' 'blind02' ...
- 'cat01' 'cat02' ...
- 'cont01' 'cont02' 'cont03' ...
- 'ctrl01' 'ctrl02'}));
+ assert(isequal(group(1).name, ''));
+ assert(isequal(group.numSub, 6));
+ assert(isequal(group.subNumber, ...
+ {'01' '02' 'blind01' 'blind02' 'ctrl01' 'ctrl02'}));
- %% Get some subjects of some groups
- opt.groups = {'cont', 'cat'};
- opt.subjects = {[1 3], 2};
+ %% Get some subjects of some groups
+ opt.groups = {'ctrl', 'blind'};
+ opt.subjects = {[1 2], 2};
- [group] = getData(opt);
+ [group] = getData(opt);
- assert(isequal(group(1).name, 'cont'));
- assert(isequal(group(1).numSub, 2));
- assert(isequal(group(1).subNumber, {'cont01' 'cont03'}));
- assert(isequal(group(2).name, 'cat'));
- assert(isequal(group(2).numSub, 1));
- assert(isequal(group(2).subNumber, {'cat02'}));
+ assert(isequal(group(1).name, 'ctrl'));
+ assert(isequal(group(1).numSub, 2));
+ assert(isequal(group(1).subNumber, {'ctrl01' 'ctrl02'}));
+ assert(isequal(group(2).name, 'blind'));
+ assert(isequal(group(2).numSub, 1));
+ assert(isequal(group(2).subNumber, {'blind02'}));
- %% Get all subjects of some groups
- opt.groups = {'cont', 'cat'};
- opt.subjects = {[], []};
+ %% Get all subjects of some groups
+ opt.groups = {'ctrl', 'blind'};
+ opt.subjects = {[], []};
- [group] = getData(opt);
+ [group] = getData(opt);
- assert(isequal(group(1).name, 'cont'));
- assert(isequal(group(1).numSub, 3));
- assert(isequal(group(1).subNumber, {'cont01' 'cont02' 'cont03'}));
- assert(isequal(group(2).name, 'cat'));
- assert(isequal(group(2).numSub, 2));
- assert(isequal(group(2).subNumber, {'cat01' 'cat02'}));
+ assert(isequal(group(1).name, 'ctrl'));
+ assert(isequal(group(1).numSub, 2));
+ assert(isequal(group(1).subNumber, {'ctrl01' 'ctrl02'}));
+ assert(isequal(group(2).name, 'blind'));
+ assert(isequal(group(2).numSub, 2));
+ assert(isequal(group(2).subNumber, {'blind01' 'blind02'}));
- %% Get some specified subjects
- opt.groups = {''};
- opt.subjects = {'01', 'cont01', 'cat02', 'ctrl02', 'blind01'};
+ %% Get some specified subjects
+ opt.groups = {''};
+ opt.subjects = {'01', 'ctrl02', 'blind02'};
- [group] = getData(opt);
+ [group] = getData(opt);
- assert(isequal(group(1).name, ''));
- assert(isequal(group(1).numSub, 5));
- assert(isequal(group(1).subNumber, {'01', 'cont01', 'cat02', 'ctrl02', 'blind01'}));
+ assert(isequal(group(1).name, ''));
+ assert(isequal(group(1).numSub, 3));
+ assert(isequal(group(1).subNumber, {'01', 'ctrl02', 'blind02'}));
- %% Only get anat metadata
- opt.groups = {''};
+ %% Only get anat metadata
+ opt.groups = {''};
- opt.subjects = {'01'};
+ opt.subjects = {'01'};
- [~, opt] = getData(opt, [], 'T1w');
+ [~, opt] = getData(opt, [], 'T1w');
- assert(isequal(opt.metadata.RepetitionTime, 2.3));
+ assert(isequal(opt.metadata.RepetitionTime, 2.3));
+
+end
+
+function test_getDataErrorTask()
+ % Small test to ensure that getData returns what we asked for
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'testTask';
+ opt.zeropad = 2;
+
+ %% Get all groups all subjects
+ opt.groups = {''};
+ opt.subjects = {[]};
+
+ assertExceptionThrown( ...
+ @()getData(opt), ...
+ 'getData:noMatchingTask');
+
+end
+
+function test_getDataErrorSubject()
+ % Small test to ensure that getData returns what we asked for
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+
+ %% Get all groups all subjects
+ opt.groups = {''};
+ opt.subjects = {'03'};
+
+ assertExceptionThrown( ...
+ @()getData(opt), ...
+ 'getData:noMatchingSubject');
end
diff --git a/tests/test_getFFXdir.m b/tests/test_getFFXdir.m
index 9437a726..5fb88422 100644
--- a/tests/test_getFFXdir.m
+++ b/tests/test_getFFXdir.m
@@ -1,41 +1,50 @@
function test_suite = test_getFFXdir %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getFFXdirBasic()
- isMVPA = false;
- funcFWFM = 0;
- subID = '01';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'raw');
- opt.taskName = 'funcLocalizer';
+ funcFWFM = 0;
+ subID = '01';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'funcLocalizer';
- expectedOutput = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
- 'SPM12_CPPL', 'sub-01', 'stats', 'ffx_task-funcLocalizer', 'ffx_FWHM-0');
+ opt = setDerivativesDir(opt);
- ffxDir = getFFXdir(subID, funcFWFM, opt, isMVPA);
+ opt = checkOptions(opt);
- assertEqual(exist(expectedOutput, 'dir'), 7);
+ expectedOutput = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
+ 'SPM12_CPPL', 'sub-01', 'stats', 'ffx_task-funcLocalizer', ...
+ 'ffx_space-MNI_FWHM-0');
+
+ ffxDir = getFFXdir(subID, funcFWFM, opt);
+
+ assertEqual(exist(expectedOutput, 'dir'), 7);
end
function test_getFFXdirMvpa()
- isMVPA = true;
- funcFWFM = 6;
- subID = '02';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'raw');
- opt.taskName = 'nBack';
+ funcFWFM = 6;
+ subID = '02';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'nBack';
+ opt.space = 'individual';
+
+ opt = setDerivativesDir(opt);
+
+ opt = checkOptions(opt);
- expectedOutput = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
- 'SPM12_CPPL', 'sub-02', 'stats', 'ffx_task-nBack', 'ffx_FWHM-6_MVPA');
+ expectedOutput = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
+ 'SPM12_CPPL', 'sub-02', 'stats', 'ffx_task-nBack', ...
+ 'ffx_space-individual_FWHM-6');
- ffxDir = getFFXdir(subID, funcFWFM, opt, isMVPA);
+ ffxDir = getFFXdir(subID, funcFWFM, opt);
- assertEqual(exist(expectedOutput, 'dir'), 7);
+ assertEqual(exist(expectedOutput, 'dir'), 7);
end
diff --git a/tests/test_getFuncVoxelDims.m b/tests/test_getFuncVoxelDims.m
new file mode 100644
index 00000000..f7800da6
--- /dev/null
+++ b/tests/test_getFuncVoxelDims.m
@@ -0,0 +1,46 @@
+function test_suite = test_getFuncVoxelDims %#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
+ end
+ initTestSuite;
+end
+
+function test_getFuncVoxelDimsBasic()
+
+ opt.funcVoxelDims = [];
+
+ subFuncDataDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demos', ...
+ 'MoAE', 'output', 'MoAEpilot', 'sub-01', 'func');
+
+ prefix = '';
+
+ fileName = 'sub-01_task-auditory_bold.nii';
+
+ [voxDim, opt] = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName);
+
+ expectedVoxDim = [3 3 3];
+ assertEqual(voxDim, expectedVoxDim);
+
+ expectedOpt.funcVoxelDims = [3 3 3];
+ assertEqual(opt, expectedOpt);
+
+end
+
+function test_getFuncVoxelDimsForce()
+
+ opt.funcVoxelDims = [1 1 1];
+
+ subFuncDataDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demos', ...
+ 'output', 'MoAEpilot', 'sub-01', 'func');
+
+ prefix = '';
+
+ fileName = 'sub-01_task-auditory_bold.nii';
+
+ voxDim = getFuncVoxelDims(opt, subFuncDataDir, prefix, fileName);
+
+ expectedVoxDim = [1 1 1];
+ assertEqual(voxDim, expectedVoxDim);
+
+end
diff --git a/tests/test_getGrpLevelContrastToCompute.m b/tests/test_getGrpLevelContrastToCompute.m
new file mode 100644
index 00000000..6ac688d0
--- /dev/null
+++ b/tests/test_getGrpLevelContrastToCompute.m
@@ -0,0 +1,40 @@
+function test_suite = test_getGrpLevelContrastToCompute %#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
+ end
+ initTestSuite;
+end
+
+function test_getGrpLevelContrastToComputeBasic()
+
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'models', 'model-visMotionLoc_smdl.json');
+
+ [grpLvlCon, iStep] = getGrpLevelContrastToCompute(opt);
+
+ AutoContrasts = {
+ 'trial_type.VisMot'; ...
+ 'trial_type.VisStat'; ...
+ 'VisMot_gt_VisStat'; ...
+ 'VisStat_gt_VisMot'};
+
+ assertEqual(iStep, 2);
+ assertEqual(grpLvlCon, AutoContrasts);
+
+ %%
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'models', 'model-vislocalizer_smdl.json');
+
+ [grpLvlCon, iStep] = getGrpLevelContrastToCompute(opt);
+
+ AutoContrasts = {
+ 'trial_type.VisMot'; ...
+ 'trial_type.VisStat'; ...
+ 'VisMot_gt_VisStat'; ...
+ 'VisStat_gt_VisMot'};
+
+ assertEqual(iStep, 3);
+ assertEqual(grpLvlCon, AutoContrasts);
+
+end
diff --git a/tests/test_getInfo.m b/tests/test_getInfo.m
index 01cee547..c94a7f6b 100644
--- a/tests/test_getInfo.m
+++ b/tests/test_getInfo.m
@@ -1,63 +1,64 @@
function test_suite = test_getInfo %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getInfoBasic()
- % Small test to ensure that getSliceOrder returns what we asked for
-
- % write tests for when no session or only one run
-
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {[], []};
-
- %% Get sessions from BIDS
- opt.taskName = 'vismotion';
- subID = 'cont02';
- info = 'sessions';
- [~, opt, BIDS] = getData(opt);
- sessions = getInfo(BIDS, subID, opt, info);
- assert(all(strcmp(sessions, {'01' '02'})));
-
- %% Get runs from BIDS
- opt.taskName = 'vismotion';
- subID = 'cont02';
- info = 'runs';
- session = '01';
- [~, opt, BIDS] = getData(opt);
- runs = getInfo(BIDS, subID, opt, info, session);
- assert(all(strcmp(runs, {'1' '2'})));
-
- %% Get runs from BIDS when no run in filename
- opt.taskName = 'vislocalizer';
- subID = 'cont02';
- info = 'runs';
- session = '01';
- [~, opt, BIDS] = getData(opt);
- runs = getInfo(BIDS, subID, opt, info, session);
- assert(strcmp(runs, {''}));
-
- %% Get filename from BIDS
- opt.taskName = 'vismotion';
- subID = 'cont02';
- session = '01';
- run = '1';
- info = 'filename';
- [~, opt, BIDS] = getData(opt);
- filename = getInfo(BIDS, subID, opt, info, session, run, 'bold');
- FileName = fullfile(fileparts(mfilename('fullpath')), 'dummyData', ...
- 'derivatives', 'SPM12_CPPL', ...
- ['sub-' subID], ['ses-' session], 'func', ...
- ['sub-' subID, ...
- '_ses-' session, ...
- '_task-' opt.taskName, ...
- '_run-' run, ...
- '_bold.nii.gz']);
-
- assert(strcmp(filename{1}, FileName));
+ % Small test to ensure that getSliceOrder returns what we asked for
+
+ % write tests for when no session or only one run
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL');
+ opt.groups = {''};
+ opt.subjects = {[], []};
+
+ %% Get sessions from BIDS
+ opt.taskName = 'vismotion';
+ subID = 'ctrl01';
+ info = 'sessions';
+ [~, opt, BIDS] = getData(opt);
+ sessions = getInfo(BIDS, subID, opt, info);
+ assert(all(strcmp(sessions, {'01' '02'})));
+
+ %% Get runs from BIDS
+ opt.taskName = 'vismotion';
+ subID = 'ctrl01';
+ info = 'runs';
+ session = '01';
+ [~, opt, BIDS] = getData(opt);
+ runs = getInfo(BIDS, subID, opt, info, session);
+ assert(all(strcmp(runs, {'1' '2'})));
+
+ %% Get runs from BIDS when no run in filename
+ opt.taskName = 'vislocalizer';
+ subID = 'ctrl01';
+ info = 'runs';
+ session = '01';
+ [~, opt, BIDS] = getData(opt);
+ runs = getInfo(BIDS, subID, opt, info, session);
+ assert(strcmp(runs, {''}));
+
+ %% Get filename from BIDS
+ opt.taskName = 'vismotion';
+ subID = 'ctrl01';
+ session = '01';
+ run = '1';
+ info = 'filename';
+ [~, opt, BIDS] = getData(opt);
+ filename = getInfo(BIDS, subID, opt, info, session, run, 'bold');
+ FileName = fullfile(fileparts(mfilename('fullpath')), 'dummyData', ...
+ 'derivatives', 'SPM12_CPPL', ...
+ ['sub-' subID], ['ses-' session], 'func', ...
+ ['sub-' subID, ...
+ '_ses-' session, ...
+ '_task-' opt.taskName, ...
+ '_run-' run, ...
+ '_bold.nii']);
+
+ assert(strcmp(filename, FileName));
end
diff --git a/tests/test_getMeanFuncFilename.m b/tests/test_getMeanFuncFilename.m
new file mode 100644
index 00000000..29e78916
--- /dev/null
+++ b/tests/test_getMeanFuncFilename.m
@@ -0,0 +1,33 @@
+function test_suite = test_getMeanFuncFilename %#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
+ end
+ initTestSuite;
+end
+
+function test_getMeanFuncFilenameBasic()
+
+ subID = '01';
+
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.groups = {''};
+ opt.subjects = {subID};
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ [meanImage, meanFuncDir] = getMeanFuncFilename(BIDS, subID, opt);
+
+ expectedMeanImage = 'meanusub-01_ses-01_task-vislocalizer_bold.nii';
+
+ expectedmeanFuncDir = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', ...
+ 'sub-01', 'ses-01', 'func');
+
+ assertEqual(meanFuncDir, expectedmeanFuncDir);
+ assertEqual(meanImage, expectedMeanImage);
+
+end
diff --git a/tests/test_getPrefix.m b/tests/test_getPrefix.m
index 207ae954..b934a10d 100644
--- a/tests/test_getPrefix.m
+++ b/tests/test_getPrefix.m
@@ -1,145 +1,145 @@
function test_suite = test_getPrefix %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getPrefixSTC()
- step = 'STC';
- funcFWHM = 6;
- opt.metadata.SliceTiming = 1:0.2:1.8;
- opt.sliceOrder = 1:10;
+ step = 'STC';
+ funcFWHM = 6;
+ opt.metadata.SliceTiming = 1:0.2:1.8;
+ opt.sliceOrder = 1:10;
- expectedPrefxOutput = '';
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = '';
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
function test_getPrefixPreprocess()
- step = 'preprocess';
- funcFWHM = 6;
- opt.metadata.SliceTiming = 1:0.2:1.8;
- opt.sliceOrder = 1:10;
+ step = 'preprocess';
+ funcFWHM = 6;
+ opt.metadata.SliceTiming = 1:0.2:1.8;
+ opt.sliceOrder = 1:10;
- expectedPrefxOutput = spm_get_defaults('slicetiming.prefix');
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = spm_get_defaults('slicetiming.prefix');
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
function test_getPrefixPreprocessNoSTC()
- step = 'preprocess';
- funcFWHM = 6;
- opt.metadata = [];
- opt.sliceOrder = [];
+ step = 'preprocess';
+ funcFWHM = 6;
+ opt.metadata = [];
+ opt.sliceOrder = [];
- expectedPrefxOutput = '';
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = '';
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
function test_getPrefixSmoothing()
- step = 'smoothing';
- funcFWHM = 6;
- opt.metadata = [];
- opt.sliceOrder = [];
+ step = 'smoothing';
+ funcFWHM = 6;
+ opt.metadata = [];
+ opt.sliceOrder = [];
- expectedPrefxOutput = spm_get_defaults('normalise.write.prefix');
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = spm_get_defaults('normalise.write.prefix');
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
-function test_getPrefixSmoothingT1w()
+function test_getPrefixSmoothingIndividual()
- step = 'smoothing_space-T1w';
- funcFWHM = 6;
- opt.metadata = [];
- opt.sliceOrder = [];
+ step = 'smoothing_space-individual';
+ funcFWHM = 6;
+ opt.metadata = [];
+ opt.sliceOrder = [];
- expectedPrefxOutput = spm_get_defaults('realign.write.prefix');
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = spm_get_defaults('unwarp.write.prefix');
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
function test_getPrefixFFX()
- step = 'FFX';
- funcFWHM = 6;
- opt.metadata = [];
- opt.sliceOrder = [];
+ step = 'FFX';
+ funcFWHM = 6;
+ opt.metadata = [];
+ opt.sliceOrder = [];
- expectedPrefxOutput = [ ...
- spm_get_defaults('smooth.prefix'), ...
- num2str(funcFWHM), ...
- spm_get_defaults('normalise.write.prefix')];
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = [ ...
+ spm_get_defaults('smooth.prefix'), ...
+ num2str(funcFWHM), ...
+ spm_get_defaults('normalise.write.prefix')];
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
-function test_getPrefixFFXT1w()
+function test_getPrefixFfxIndividual()
- step = 'FFX_space-T1w';
- funcFWHM = 6;
- opt.metadata = [];
- opt.sliceOrder = [];
+ step = 'FFX_space-individual';
+ funcFWHM = 6;
+ opt.metadata = [];
+ opt.sliceOrder = [];
- expectedPrefxOutput = [ ...
- spm_get_defaults('smooth.prefix'), ...
- num2str(funcFWHM), ...
- spm_get_defaults('realign.write.prefix')];
- expectedMotionRegressorPrefix = '';
+ expectedPrefxOutput = [ ...
+ spm_get_defaults('smooth.prefix'), ...
+ num2str(funcFWHM), ...
+ spm_get_defaults('unwarp.write.prefix')];
+ expectedMotionRegressorPrefix = '';
- [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
+ [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM);
- assertEqual(expectedPrefxOutput, prefix);
- assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
+ assertEqual(expectedPrefxOutput, prefix);
+ assertEqual(expectedMotionRegressorPrefix, motionRegressorPrefix);
end
function test_getPrefixError()
- step = 'error';
- funcFWHM = 6;
- opt.metadata = [];
- opt.sliceOrder = [];
+ step = 'error';
+ funcFWHM = 6;
+ opt.metadata = [];
+ opt.sliceOrder = [];
- assertExceptionThrown( ...
- @()getPrefix(step, opt, funcFWHM), ...
- 'getPrefix:unknownPrefixCase');
+ assertExceptionThrown( ...
+ @()getPrefix(step, opt, funcFWHM), ...
+ 'getPrefix:unknownPrefixCase');
end
diff --git a/tests/test_getRFXdir.m b/tests/test_getRFXdir.m
index 6c0589c7..d5014cb2 100644
--- a/tests/test_getRFXdir.m
+++ b/tests/test_getRFXdir.m
@@ -1,32 +1,35 @@
function test_suite = test_getRFXdir %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getRFXdirBasic()
- funcFWHM = 0;
- conFWHM = 0;
+ funcFWHM = 0;
+ conFWHM = 0;
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'raw');
- opt.taskName = 'funcLocalizer';
- contrastName = 'stim_gt_baseline';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'funcLocalizer';
- rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
+ opt = setDerivativesDir(opt);
- expectedOutput = fullfile( ...
- fileparts(mfilename('fullpath')), ...
- 'dummyData', ...
- 'derivatives', ...
- 'SPM12_CPPL', ...
- 'group', ...
- 'rfx_task-funcLocalizer', ...
- 'rfx_funcFWHM-0_conFWHM-0', ...
- 'stim_gt_baseline');
+ contrastName = 'stim_gt_baseline';
- assertEqual(exist(expectedOutput, 'dir'), 7);
+ rfxDir = getRFXdir(opt, funcFWHM, conFWHM, contrastName);
+
+ expectedOutput = fullfile( ...
+ fileparts(mfilename('fullpath')), ...
+ 'dummyData', ...
+ 'derivatives', ...
+ 'SPM12_CPPL', ...
+ 'group', ...
+ 'rfx_task-funcLocalizer', ...
+ 'rfx_funcFWHM-0_conFWHM-0', ...
+ 'stim_gt_baseline');
+
+ assertEqual(exist(expectedOutput, 'dir'), 7);
end
diff --git a/tests/test_getRealignParamFile.m b/tests/test_getRealignParamFile.m
index 123ad729..ba16fc1b 100644
--- a/tests/test_getRealignParamFile.m
+++ b/tests/test_getRealignParamFile.m
@@ -1,82 +1,89 @@
function test_suite = test_getRealignParamFile %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getRealignParamFileBasic()
- subID = '01';
- funcFWHM = 6;
- iSes = 1;
- iRun = 1;
+ subID = '01';
+ session = '01';
+ run = '';
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.subjects = {subID};
- [~, opt, BIDS] = getData(opt);
+ opt = checkOptions(opt);
- boldFileName = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
- realignParamFile = getRealignParamFile(opt, boldFileName, funcFWHM);
+ [~, opt, BIDS] = getData(opt);
- expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'ses-01', 'func', ...
- 'rp_sub-01_ses-01_task-vislocalizer_bold.txt');
+ [boldFileName, subFuncDataDir] = getBoldFilename(BIDS, subID, session, run, opt);
+ realignParamFile = getRealignParamFile(fullfile(subFuncDataDir, boldFileName));
- assertEqual(expectedFileName, realignParamFile);
+ expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', ...
+ 'ses-01', 'func', ...
+ 'rp_sub-01_ses-01_task-vislocalizer_bold.txt');
+
+ assertEqual(expectedFileName, realignParamFile);
end
function test_getRealignParamFileNativeSpace()
- subID = '01';
- funcFWHM = 6;
- iSes = 1;
- iRun = 1;
+ subID = '01';
+ session = '01';
+ run = '';
+
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.subjects = {subID};
+ opt.space = 'T1w';
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
- opt.space = 'T1w';
+ opt = checkOptions(opt);
- [~, opt, BIDS] = getData(opt);
+ [~, opt, BIDS] = getData(opt);
- boldFileName = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
- realignParamFile = getRealignParamFile(opt, boldFileName, funcFWHM);
+ [boldFileName, subFuncDataDir] = getBoldFilename(BIDS, subID, session, run, opt);
+ realignParamFile = getRealignParamFile(fullfile(subFuncDataDir, boldFileName));
- expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', 'ses-01', 'func', ...
- 'rp_sub-01_ses-01_task-vislocalizer_bold.txt');
+ expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', ...
+ 'ses-01', 'func', ...
+ 'rp_sub-01_ses-01_task-vislocalizer_bold.txt');
- assertEqual(expectedFileName, realignParamFile);
+ assertEqual(expectedFileName, realignParamFile);
end
-function test_getRealignParamFileError()
+function test_getRealignParamFileFFX()
+
+ subID = '01';
+ funcFWHM = 6;
+ iSes = 1;
+ iRun = 1;
+
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.subjects = {subID};
+ opt.space = 'T1w';
- subID = '01';
- goodFuncFWHM = 6;
- badFuncFWHM = 8;
- iSes = 1;
- iRun = 1;
+ opt = checkOptions(opt);
- opt.taskName = 'vislocalizer';
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {'01'};
- opt.space = 'T1w';
+ [~, opt, BIDS] = getData(opt);
- [~, opt, BIDS] = getData(opt);
+ [boldFileName, prefix] = getBoldFilenameForFFX(BIDS, opt, subID, funcFWHM, iSes, iRun);
+ [subFuncDataDir, boldFileName, ext] = spm_fileparts(boldFileName);
+ realignParamFile = getRealignParamFile(fullfile(subFuncDataDir, [boldFileName, ext]), prefix);
- boldFileName = getBoldFilenameForFFX(BIDS, opt, subID, goodFuncFWHM, iSes, iRun);
+ expectedFileName = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', 'sub-01', ...
+ 'ses-01', 'func', ...
+ 'rp_sub-01_ses-01_task-vislocalizer_bold.txt');
- assertExceptionThrown( ...
- @()getRealignParamFile(opt, boldFileName, badFuncFWHM), ...
- 'getRealignParamFile:nonExistentFile');
+ assertEqual(expectedFileName, realignParamFile);
end
diff --git a/tests/test_getSliceOrder.m b/tests/test_getSliceOrder.m
index ffb77c93..25e06a14 100644
--- a/tests/test_getSliceOrder.m
+++ b/tests/test_getSliceOrder.m
@@ -1,52 +1,71 @@
function test_suite = test_getSliceOrder %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_getSliceOrderBasic()
- % Small test to ensure that getSliceOrder returns what we asked for
-
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.groups = {''};
- opt.subjects = {[], []};
-
- %% Get slice order from BIDS
- sliceOrder = repmat( ...
- [0.5475; ...
- 0; ...
- 0.3825; ...
- 0.0550; ...
- 0.4375; ...
- 0.1100; ...
- 0.4925; ...
- 0.2200; ...
- 0.6025; ...
- 0.2750; ...
- 0.6575; ...
- 0.3275; ...
- 0.7100; ...
- 0.1650], [3, 1]);
-
- opt.taskName = 'vismotion';
- [~, opt] = getData(opt);
- BIDS_sliceOrder = getSliceOrder(opt, 0);
- assert(isequal(sliceOrder, BIDS_sliceOrder));
-
- %% Get empty slice order from BIDS
- opt.taskName = 'vislocalizer';
- [~, opt] = getData(opt);
- BIDS_sliceOrder = getSliceOrder(opt, 0);
- assert(isempty(BIDS_sliceOrder));
-
- %% Get slice order from options
- opt.STC_referenceSlice = 1000;
- opt.sliceOrder = 0:250:2000;
- opt.taskName = 'vislocalizer';
- [~, opt] = getData(opt);
- BIDS_sliceOrder = getSliceOrder(opt, 0);
- assert(isequal(BIDS_sliceOrder, opt.sliceOrder));
+ % Small test to ensure that getSliceOrder returns what we asked for
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+
+ opt = checkOptions(opt);
+
+ [~, opt] = getData(opt);
+ BIDS_sliceOrder = getSliceOrder(opt, 0);
+
+ %% Get slice order from BIDS
+ sliceOrder = repmat( ...
+ [0.5475; ...
+ 0; ...
+ 0.3825; ...
+ 0.0550; ...
+ 0.4375; ...
+ 0.1100; ...
+ 0.4925; ...
+ 0.2200; ...
+ 0.6025; ...
+ 0.2750; ...
+ 0.6575; ...
+ 0.3275; ...
+ 0.7100; ...
+ 0.1650], [3, 1]);
+
+ assert(isequal(sliceOrder, BIDS_sliceOrder));
+
+end
+
+function test_getSliceOrderEmpty()
+
+ %% Get empty slice order from BIDS
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vislocalizer';
+
+ opt = checkOptions(opt);
+
+ [~, opt] = getData(opt);
+
+ BIDS_sliceOrder = getSliceOrder(opt, 0);
+
+ assert(isempty(BIDS_sliceOrder));
+
+end
+
+function test_getSliceOrderFromOptions()
+
+ %% Get slice order from options
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.STC_referenceSlice = 1000;
+ opt.sliceOrder = 0:250:2000;
+ opt.taskName = 'vislocalizer';
+
+ opt = checkOptions(opt);
+
+ [~, opt] = getData(opt);
+ BIDS_sliceOrder = getSliceOrder(opt, 0);
+ assert(isequal(BIDS_sliceOrder, opt.sliceOrder));
end
diff --git a/tests/test_inputFileValidation.m b/tests/test_inputFileValidation.m
deleted file mode 100644
index 8c3ccacb..00000000
--- a/tests/test_inputFileValidation.m
+++ /dev/null
@@ -1,36 +0,0 @@
-function test_suite = test_inputFileValidation %#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
- end
- initTestSuite;
-end
-
-function test_inputFileValidationBasic()
-
- directory = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
- 'SPM12_CPPL', 'sub-01', 'ses-01', 'func');
- prefix = '';
- fileName = 'sub-01_ses-01_task-vislocalizer_bold.nii.gz';
-
- expectedOutput = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
- 'SPM12_CPPL', 'sub-01', 'ses-01', 'func', ...
- 'sub-01_ses-01_task-vislocalizer_bold.nii.gz');
-
- file = inputFileValidation(directory, prefix, fileName);
-
- assertEqual(expectedOutput, file{1});
-
-end
-
-function test_inputFileValidationError()
-
- directory = pwd;
- prefix = 'swa';
- fileName = 'gibberish.nii.gz';
-
- assertExceptionThrown( ...
- @()inputFileValidation(directory, prefix, fileName), ...
- 'inputFileValidation:nonExistentFile');
-
-end
diff --git a/tests/test_loadAndCheckOptions.m b/tests/test_loadAndCheckOptions.m
new file mode 100644
index 00000000..ca8c4673
--- /dev/null
+++ b/tests/test_loadAndCheckOptions.m
@@ -0,0 +1,132 @@
+function test_suite = test_loadAndCheckOptions %#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
+ end
+ initTestSuite;
+end
+
+function test_loadAndCheckOptionsBasic()
+
+ delete('*.json');
+
+ % create dummy json file
+ jsonContent.taskName = 'vismotion';
+ filename = 'options_task-vismotion.json';
+ spm_jsonwrite(filename, jsonContent);
+
+ % makes sure that it is picked up by default
+ opt = loadAndCheckOptions();
+
+ expectedOptions = defaultOptions();
+ expectedOptions.taskName = 'vismotion';
+
+ assertEqual(opt, expectedOptions);
+
+end
+
+function test_loadAndCheckOptionsFromFile()
+
+ delete('*.json');
+
+ % create dummy json file
+ jsonContent.taskName = 'vismotion';
+ jsonContent.space = 'individual';
+ filename = 'options_task-vismotion_space-T1w.json';
+ spm_jsonwrite(filename, jsonContent);
+
+ % makes sure that it is read correctly from
+ opt = loadAndCheckOptions('options_task-vismotion_space-T1w.json');
+
+ expectedOptions = defaultOptions();
+ expectedOptions.taskName = 'vismotion';
+ expectedOptions.space = 'individual';
+
+ assertEqual(opt, expectedOptions);
+
+ delete('*.json');
+
+end
+
+function test_loadAndCheckOptionsFromSeveralFiles()
+
+ delete('*.json');
+
+ % create old dummy json file
+ jsonContent.taskName = 'vismotion';
+ filename = fullfile(pwd, ['options', ...
+ '_task-', jsonContent.taskName, ...
+ '_date-151501011111', ...
+ '.json']);
+ spm_jsonwrite(filename, jsonContent);
+
+ % create dummy json file with no date
+ jsonContent.taskName = 'vismotion';
+ jsonContent.space = 'individual';
+ filename = 'options_task-vismotion_space-T1w.json';
+ spm_jsonwrite(filename, jsonContent);
+
+ % most recent option file that should be read from
+ jsonContent.taskName = 'vismotion';
+ jsonContent.space = 'individual';
+ jsonContent.funcVoxelDims = [1 1 1];
+ filename = fullfile(pwd, ['options', ...
+ '_task-', jsonContent.taskName, ...
+ '_date-' datestr(now, 'yyyymmddHHMM'), ...
+ '.json']);
+ spm_jsonwrite(filename, jsonContent);
+
+ % makes sure that the right json is read
+ opt = loadAndCheckOptions();
+
+ expectedOptions = defaultOptions();
+ expectedOptions.taskName = 'vismotion';
+ expectedOptions.space = 'individual';
+ expectedOptions.funcVoxelDims = [1 1 1]';
+
+ assertEqual(opt, expectedOptions);
+
+end
+
+function expectedOptions = defaultOptions()
+
+ expectedOptions.sliceOrder = [];
+ expectedOptions.STC_referenceSlice = [];
+
+ expectedOptions.dataDir = '';
+ expectedOptions.derivativesDir = '';
+
+ expectedOptions.funcVoxelDims = [];
+
+ expectedOptions.groups = {''};
+ expectedOptions.subjects = {[]};
+
+ expectedOptions.space = 'MNI';
+
+ expectedOptions.anatReference.type = 'T1w';
+ expectedOptions.anatReference.session = 1;
+
+ expectedOptions.skullstrip.threshold = 0.75;
+
+ expectedOptions.ignoreFieldmaps = false;
+
+ expectedOptions.taskName = '';
+
+ expectedOptions.zeropad = 2;
+
+ expectedOptions.contrastList = {};
+ expectedOptions.model.file = '';
+
+ expectedOptions.result.Steps = struct( ...
+ 'Level', '', ...
+ 'Contrasts', struct( ...
+ 'Name', '', ...
+ 'Mask', false, ...
+ 'MC', 'FWE', ...
+ 'p', 0.05, ...
+ 'k', 0, ...
+ 'NIDM', true));
+
+ expectedOptions = orderfields(expectedOptions);
+
+end
diff --git a/tests/test_modelFiles.m b/tests/test_modelFiles.m
index f14f429d..4c442b60 100644
--- a/tests/test_modelFiles.m
+++ b/tests/test_modelFiles.m
@@ -1,60 +1,45 @@
function test_suite = test_modelFiles %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_modelFilesBasic()
- % vague attempt at validating our model files
+ % vague attempt at validating our model files
- demoDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demo');
- modelDir = fullfile(fileparts(mfilename('fullpath')), '..', 'model');
+ demoDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demos');
- %%
- file = fullfile(demoDir, 'model-MoAE_smdl.json');
+ %%
+ file = fullfile(demoDir, 'MoAE', 'models', 'model-MoAE_smdl.json');
- model = spm_jsonread(file);
+ model = spm_jsonread(file);
- model.Steps{1};
+ model.Steps{1};
- %%
- file = fullfile(fileparts(mfilename('fullpath')), '..', ...
- 'model-visMotionLoc_smdl.json');
+ %%
+ file = fullfile(demoDir, 'vismotion', 'models', ...
+ 'model-visMotionLoc_smdl.json');
- model = spm_jsonread(file);
+ model = spm_jsonread(file);
- model.Steps{1};
+ model.Steps{1};
- %%
- file = fullfile(fileparts(mfilename('fullpath')), '..', ...
- 'model-motionDecodingUnivariate_smdl.json');
+ %%
+ file = fullfile(demoDir, 'vismotion', 'models', ...
+ 'model-motionDecodingUnivariate_smdl.json');
- model = spm_jsonread(file);
+ model = spm_jsonread(file);
- model.Steps{1};
+ model.Steps{1};
- %%
- file = fullfile(fileparts(mfilename('fullpath')), '..', ...
- 'model-motionDecodingMultivariate_smdl.json');
+ %%
+ file = fullfile(demoDir, 'vismotion', 'models', ...
+ 'model-motionDecodingMultivariate_smdl.json');
- model = spm_jsonread(file);
+ model = spm_jsonread(file);
- model.Steps{1};
-
- %%
- file = fullfile(modelDir, 'model-balloonanalogriskUnivariate_smdl.json');
-
- model = spm_jsonread(file);
-
- model.Steps{1};
-
- %%
- file = fullfile(modelDir, 'model-balloonanalogriskMultivariate_smdl.json');
-
- model = spm_jsonread(file);
-
- model.Steps{1};
+ model.Steps{1};
end
diff --git a/tests/test_saveMatlabBatch.m b/tests/test_saveMatlabBatch.m
index 51acc575..a05c72a7 100644
--- a/tests/test_saveMatlabBatch.m
+++ b/tests/test_saveMatlabBatch.m
@@ -1,36 +1,38 @@
function test_suite = test_saveMatlabBatch %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_saveMatlabBatchBasic()
- opt.jobsDir = pwd;
- subID = '01';
- matlabbatch = struct('test', 1);
+ opt.jobsDir = pwd;
+ subID = '01';
+ matlabbatch = struct('test', 1);
- expectedOutput = fullfile(pwd, 'sub-01', ...
- [datestr(now, 'yyyymmdd_HHMM') '_jobs_matlabbatch_SPM12_test.mat']);
+ expectedOutput = fullfile(pwd, 'sub-01', ...
+ [datestr(now, 'yyyymmdd_HHMM') ...
+ '_jobs_matlabbatch_SPM12_test.mat']);
- saveMatlabBatch(matlabbatch, 'test', opt, subID);
+ saveMatlabBatch(matlabbatch, 'test', opt, subID);
- assertEqual(exist(expectedOutput, 'file'), 2);
+ assertEqual(exist(expectedOutput, 'file'), 2);
end
function test_saveMatlabBatchGroup()
- opt.jobsDir = pwd;
- matlabbatch = struct('test', 1);
+ opt.jobsDir = pwd;
+ matlabbatch = struct('test', 1);
- expectedOutput = fullfile(pwd, 'group', ...
- [datestr(now, 'yyyymmdd_HHMM') '_jobs_matlabbatch_SPM12_groupTest.mat']);
+ expectedOutput = fullfile(pwd, 'group', ...
+ [datestr(now, 'yyyymmdd_HHMM') ...
+ '_jobs_matlabbatch_SPM12_groupTest.mat']);
- saveMatlabBatch(matlabbatch, 'groupTest', opt);
+ saveMatlabBatch(matlabbatch, 'groupTest', opt);
- assertEqual(exist(expectedOutput, 'file'), 2);
+ assertEqual(exist(expectedOutput, 'file'), 2);
end
diff --git a/tests/test_setBatchCoregistrationFuncToAnat.m b/tests/test_setBatchCoregistrationFuncToAnat.m
new file mode 100644
index 00000000..138029a1
--- /dev/null
+++ b/tests/test_setBatchCoregistrationFuncToAnat.m
@@ -0,0 +1,127 @@
+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
+ end
+ initTestSuite;
+end
+
+function test_setBatchCoregistrationFuncToAnatBasic()
+
+ % necessarry to deal with SPM module dependencies
+ spm_jobman('initcfg');
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '02';
+
+ opt.orderBatches.selectAnat = 1;
+ opt.orderBatches.realign = 2;
+
+ matlabbatch = {};
+ matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt);
+
+ nbRuns = 4;
+
+ meanImageToUse = 'rmean';
+ otherImageToUse = 'cfiles';
+
+ expectedBatch = returnExpectedBatch(nbRuns, meanImageToUse, otherImageToUse);
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.coreg.estimate.ref, ...
+ expectedBatch{1}.spm.spatial.coreg.estimate.ref);
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.coreg.estimate.source, ...
+ expectedBatch{1}.spm.spatial.coreg.estimate.source);
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.coreg.estimate.other, ...
+ expectedBatch{1}.spm.spatial.coreg.estimate.other);
+
+end
+
+function test_setBatchCoregistrationFuncToAnatNative()
+
+ % necessarry to deal with SPM module dependencies
+ spm_jobman('initcfg');
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+ opt.space = 'individual';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '02';
+
+ opt.orderBatches.selectAnat = 1;
+ opt.orderBatches.realign = 2;
+
+ matlabbatch = {};
+ matlabbatch = setBatchCoregistrationFuncToAnat(matlabbatch, BIDS, subID, opt);
+
+ nbRuns = 4;
+
+ meanImageToUse = 'meanuwr';
+ otherImageToUse = 'uwrfiles';
+
+ expectedBatch = returnExpectedBatch(nbRuns, meanImageToUse, otherImageToUse);
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.coreg.estimate.ref, ...
+ expectedBatch{1}.spm.spatial.coreg.estimate.ref);
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.coreg.estimate.source, ...
+ expectedBatch{1}.spm.spatial.coreg.estimate.source);
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.coreg.estimate.other, ...
+ expectedBatch{1}.spm.spatial.coreg.estimate.other);
+
+end
+
+function expectedBatch = returnExpectedBatch(nbRuns, meanImageToUse, otherImageToUse)
+
+ expectedBatch = {};
+
+ expectedBatch{end + 1}.spm.spatial.coreg.estimate.ref(1) = ...
+ cfg_dep('Named File Selector: Anatomical(1) - Files', ...
+ substruct( ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'files', '{}', {1}));
+
+ expectedBatch{end}.spm.spatial.coreg.estimate.source(1) = ...
+ cfg_dep('Realign: Estimate & Reslice/Unwarp: Mean Image', ...
+ substruct( ...
+ '.', 'val', '{}', {2}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', meanImageToUse));
+
+ for iRun = 1:nbRuns
+
+ stringToUse = sprintf( ...
+ 'Realign: Estimate & Reslice/Unwarp: Realigned Images (Sess %i)', ...
+ iRun);
+
+ expectedBatch{end}.spm.spatial.coreg.estimate.other(iRun) = ...
+ cfg_dep(stringToUse, ...
+ substruct( ...
+ '.', 'val', '{}', {2}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'sess', '()', {iRun}, ...
+ '.', otherImageToUse));
+
+ end
+
+end
diff --git a/tests/test_setBatchNormalizationSpatialPrepro.m b/tests/test_setBatchNormalizationSpatialPrepro.m
new file mode 100644
index 00000000..2d69117c
--- /dev/null
+++ b/tests/test_setBatchNormalizationSpatialPrepro.m
@@ -0,0 +1,98 @@
+function test_suite = test_setBatchNormalizationSpatialPrepro %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchNormalizationSpatialPreproBasic()
+
+ % necessarry to deal with SPM module dependencies
+ spm_jobman('initcfg');
+
+ opt.orderBatches.coregister = 3;
+ opt.orderBatches.segment = 5;
+
+ matlabbatch = {};
+ voxDim = [3 3 3];
+ matlabbatch = setBatchNormalizationSpatialPrepro(matlabbatch, voxDim, opt);
+
+ expectedBatch = returnExpectedBatch(voxDim);
+
+ assertEqual(expectedBatch, matlabbatch);
+
+end
+
+function expectedBatch = returnExpectedBatch(voxDim)
+
+ expectedBatch = {};
+
+ jobsToAdd = numel(expectedBatch) + 1;
+
+ for iJob = jobsToAdd:(jobsToAdd + 4)
+ expectedBatch{iJob}.spm.spatial.normalise.write.subj.def(1) = ...
+ cfg_dep('Segment: Forward Deformations', ...
+ substruct( ...
+ '.', 'val', '{}', {5}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'fordef', '()', {':'})); %#ok<*AGROW>
+
+ end
+
+ expectedBatch{jobsToAdd}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Coregister: Estimate: Coregistered Images', ...
+ substruct( ...
+ '.', 'val', '{}', {3}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'cfiles'));
+ expectedBatch{jobsToAdd}.spm.spatial.normalise.write.woptions.vox = voxDim;
+
+ expectedBatch{jobsToAdd + 1}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: Bias Corrected (1)', ...
+ substruct( ...
+ '.', 'val', '{}', {5}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'channel', '()', {1}, ...
+ '.', 'biascorr', '()', {':'}));
+ expectedBatch{jobsToAdd + 1}.spm.spatial.normalise.write.woptions.vox = [1 1 1];
+
+ expectedBatch{jobsToAdd + 2}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: c1 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {5}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {1}, ...
+ '.', 'c', '()', {':'}));
+ expectedBatch{jobsToAdd + 2}.spm.spatial.normalise.write.woptions.vox = voxDim;
+
+ expectedBatch{jobsToAdd + 3}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: c2 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {5}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {2}, ...
+ '.', 'c', '()', {':'}));
+ expectedBatch{jobsToAdd + 3}.spm.spatial.normalise.write.woptions.vox = voxDim;
+
+ expectedBatch{jobsToAdd + 4}.spm.spatial.normalise.write.subj.resample(1) = ...
+ cfg_dep('Segment: c3 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {5}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {3}, ...
+ '.', 'c', '()', {':'}));
+ expectedBatch{jobsToAdd + 4}.spm.spatial.normalise.write.woptions.vox = voxDim;
+
+end
diff --git a/tests/test_setBatchRealign.m b/tests/test_setBatchRealign.m
new file mode 100644
index 00000000..bed3aa26
--- /dev/null
+++ b/tests/test_setBatchRealign.m
@@ -0,0 +1,45 @@
+function test_suite = test_setBatchRealign %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchRealignBasic()
+
+ % TODO
+ % need a test with several sessions and runs
+ % add test realign and reslice
+ % add test realign and unwarp
+ % check it returns the right voxDim
+
+ opt.dataDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demos', ...
+ 'MoAE', 'output', 'MoAEpilot');
+ opt.taskName = 'auditory';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '01';
+
+ matlabbatch = [];
+ matlabbatch = setBatchRealign(matlabbatch, BIDS, subID, opt);
+
+ expectedBatch{1}.spm.spatial.realign.estwrite.eoptions.weight = {''};
+ expectedBatch{end}.spm.spatial.realign.estwrite.roptions.which = [0 1];
+
+ runCounter = 1;
+ for iSes = 1
+ fileName = spm_BIDS(BIDS, 'data', ...
+ 'sub', subID, ...
+ 'task', opt.taskName, ...
+ 'type', 'bold');
+
+ expectedBatch{1}.spm.spatial.realign.estwrite.data{iSes} = cellstr(fileName);
+ end
+
+ assertEqual(matlabbatch, expectedBatch);
+
+end
diff --git a/tests/test_setBatchSTC.m b/tests/test_setBatchSTC.m
new file mode 100644
index 00000000..bdb66312
--- /dev/null
+++ b/tests/test_setBatchSTC.m
@@ -0,0 +1,109 @@
+function test_suite = test_setBatchSTC %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSTCEmpty()
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vislocalizer';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '02';
+ matlabbatch = setBatchSTC(BIDS, opt, subID);
+
+ % no slice timing info for this run so nothing should be returned.
+ assertEqual(matlabbatch, []);
+
+end
+
+function test_setBatchSTCForce()
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vislocalizer';
+ % we give it some slice timing value to force slice timing to happen
+ opt.sliceOrder = 1:5;
+ opt.STC_referenceSlice = 1.24 / 2;
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '02';
+ matlabbatch = setBatchSTC(BIDS, opt, subID);
+
+ TR = 1.55;
+ expectedBatch = returnExpectedBatch(opt.sliceOrder, opt.STC_referenceSlice, TR);
+
+ runCounter = 1;
+ for iSes = 1:2
+ fileName = spm_BIDS(BIDS, 'data', ...
+ 'sub', subID, ...
+ 'ses', sprintf('0%i', iSes), ...
+ 'task', opt.taskName, ...
+ 'type', 'bold');
+ expectedBatch{1}.spm.temporal.st.scans{runCounter} = {fileName{1}};
+ runCounter = runCounter + 1;
+ end
+
+ assertEqual(matlabbatch, expectedBatch);
+
+end
+
+function test_setBatchSTCBasic()
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '02';
+ matlabbatch = setBatchSTC(BIDS, opt, subID);
+
+ TR = 1.5;
+ sliceOrder = repmat([ ...
+ 0.5475, 0, 0.3825, 0.055, 0.4375, 0.11, 0.4925, 0.22, 0.6025, ...
+ 0.275, 0.6575, ...
+ 0.3275, 0.71, 0.165], 1, 3)';
+ STC_referenceSlice = 0.355;
+
+ expectedBatch = returnExpectedBatch(sliceOrder, STC_referenceSlice, TR);
+
+ runCounter = 1;
+ for iSes = 1:2
+ fileName = spm_BIDS(BIDS, 'data', ...
+ 'sub', subID, ...
+ 'ses', sprintf('0%i', iSes), ...
+ 'task', opt.taskName, ...
+ 'type', 'bold');
+ expectedBatch{1}.spm.temporal.st.scans{runCounter} = ...
+ {fileName{1}};
+ expectedBatch{1}.spm.temporal.st.scans{runCounter + 1} = ...
+ {fileName{2}};
+ runCounter = runCounter + 2;
+ end
+
+ assertEqual(matlabbatch, expectedBatch);
+
+end
+
+function expectedBatch = returnExpectedBatch(sliceOrder, referenceSlice, TR)
+
+ nbSlices = length(sliceOrder);
+ TA = TR - (TR / nbSlices);
+
+ expectedBatch{1}.spm.temporal.st.nslices = nbSlices;
+ expectedBatch{1}.spm.temporal.st.tr = TR;
+ expectedBatch{1}.spm.temporal.st.ta = TA;
+ expectedBatch{1}.spm.temporal.st.so = sliceOrder;
+ expectedBatch{1}.spm.temporal.st.refslice = referenceSlice;
+
+end
diff --git a/tests/test_setBatchSaveCoregistrationMatrix.m b/tests/test_setBatchSaveCoregistrationMatrix.m
new file mode 100644
index 00000000..8b1b7850
--- /dev/null
+++ b/tests/test_setBatchSaveCoregistrationMatrix.m
@@ -0,0 +1,63 @@
+function test_suite = test_setBatchSaveCoregistrationMatrix %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSaveCoregistrationMatrixBasic()
+
+ % necessarry to deal with SPM module dependencies
+ spm_jobman('initcfg');
+
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'vismotion';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+ subID = '02';
+
+ opt.orderBatches.coregister = 1;
+
+ matlabbatch = {};
+ matlabbatch = setBatchSaveCoregistrationMatrix(matlabbatch, BIDS, subID, opt);
+
+ expectedBatch = returnExpectedBatch();
+ assertEqual(matlabbatch, expectedBatch);
+
+end
+
+function expectedBatch = returnExpectedBatch()
+
+ expectedBatch = {};
+
+ subFuncDataDir = fullfile(fileparts( ...
+ mfilename('fullpath')), ...
+ 'dummyData', ...
+ 'derivatives', ...
+ 'SPM12_CPPL', ...
+ 'sub-02', ...
+ 'ses-01', ...
+ 'func');
+
+ fileName = 'sub-02_ses-01_task-vismotion_run-1_from-scanner_to-T1w_mode-image_xfm.mat';
+
+ expectedBatch{end + 1}.cfg_basicio.var_ops.cfg_save_vars.name = fileName;
+ expectedBatch{end}.cfg_basicio.var_ops.cfg_save_vars.outdir = {subFuncDataDir};
+ expectedBatch{end}.cfg_basicio.var_ops.cfg_save_vars.vars.vname = 'transformationMatrix';
+
+ expectedBatch{end}.cfg_basicio.var_ops.cfg_save_vars.vars.vcont(1) = ...
+ cfg_dep( ...
+ 'Coregister: Estimate: Coregistration Matrix', ...
+ substruct( ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'M'));
+
+ expectedBatch{end}.cfg_basicio.var_ops.cfg_save_vars.saveasstruct = false;
+
+end
diff --git a/tests/test_setBatchSegmentation.m b/tests/test_setBatchSegmentation.m
new file mode 100644
index 00000000..ac5f221f
--- /dev/null
+++ b/tests/test_setBatchSegmentation.m
@@ -0,0 +1,87 @@
+function test_suite = test_setBatchSegmentation %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSegmentationBasic()
+
+ spmLocation = spm('dir');
+
+ % necessarry to deal with SPM module dependencies
+ spm_jobman('initcfg');
+
+ opt.orderBatches.selectAnat = 1;
+
+ matlabbatch = [];
+ matlabbatch = setBatchSegmentation(matlabbatch, opt);
+
+ expectedBatch = returnExpectedBatch(spmLocation);
+
+ assertEqual(expectedBatch, matlabbatch);
+
+end
+
+function expectedBatch = returnExpectedBatch(spmLocation)
+
+ expectedBatch = [];
+
+ expectedBatch{end + 1}.spm.spatial.preproc.channel.vols(1) = ...
+ cfg_dep('Named File Selector: Anatomical(1) - Files', ...
+ substruct( ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct('.', 'files', '{}', {1}));
+ expectedBatch{end}.spm.spatial.preproc.channel.biasreg = 0.001;
+ expectedBatch{end}.spm.spatial.preproc.channel.biasfwhm = 60;
+ expectedBatch{end}.spm.spatial.preproc.channel.write = [0 1];
+
+ expectedBatch{end}.spm.spatial.preproc.tissue(1).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',1']};
+ expectedBatch{end}.spm.spatial.preproc.tissue(1).ngaus = 1;
+ expectedBatch{end}.spm.spatial.preproc.tissue(1).native = [1 1];
+ expectedBatch{end}.spm.spatial.preproc.tissue(1).warped = [0 0];
+
+ expectedBatch{end}.spm.spatial.preproc.tissue(2).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',2']};
+ expectedBatch{end}.spm.spatial.preproc.tissue(2).ngaus = 1;
+ expectedBatch{end}.spm.spatial.preproc.tissue(2).native = [1 1];
+ expectedBatch{end}.spm.spatial.preproc.tissue(2).warped = [0 0];
+
+ expectedBatch{end}.spm.spatial.preproc.tissue(3).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',3']};
+ expectedBatch{end}.spm.spatial.preproc.tissue(3).ngaus = 2;
+ expectedBatch{end}.spm.spatial.preproc.tissue(3).native = [1 1];
+ expectedBatch{end}.spm.spatial.preproc.tissue(3).warped = [0 0];
+
+ expectedBatch{end}.spm.spatial.preproc.tissue(4).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',4']};
+ expectedBatch{end}.spm.spatial.preproc.tissue(4).ngaus = 3;
+ expectedBatch{end}.spm.spatial.preproc.tissue(4).native = [0 0];
+ expectedBatch{end}.spm.spatial.preproc.tissue(4).warped = [0 0];
+
+ expectedBatch{end}.spm.spatial.preproc.tissue(5).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',5']};
+ expectedBatch{end}.spm.spatial.preproc.tissue(5).ngaus = 4;
+ expectedBatch{end}.spm.spatial.preproc.tissue(5).native = [0 0];
+ expectedBatch{end}.spm.spatial.preproc.tissue(5).warped = [0 0];
+
+ expectedBatch{end}.spm.spatial.preproc.tissue(6).tpm = ...
+ {[fullfile(spmLocation, 'tpm', 'TPM.nii') ',6']};
+ expectedBatch{end}.spm.spatial.preproc.tissue(6).ngaus = 2;
+ expectedBatch{end}.spm.spatial.preproc.tissue(6).native = [0 0];
+ expectedBatch{end}.spm.spatial.preproc.tissue(6).warped = [0 0];
+
+ expectedBatch{end}.spm.spatial.preproc.warp.mrf = 1;
+ expectedBatch{end}.spm.spatial.preproc.warp.cleanup = 1;
+ expectedBatch{end}.spm.spatial.preproc.warp.reg = [0 0.001 0.5 0.05 0.2];
+ expectedBatch{end}.spm.spatial.preproc.warp.affreg = 'mni';
+ expectedBatch{end}.spm.spatial.preproc.warp.fwhm = 0;
+ expectedBatch{end}.spm.spatial.preproc.warp.samp = 3;
+ expectedBatch{end}.spm.spatial.preproc.warp.write = [1 1];
+
+end
diff --git a/tests/test_setBatchSelectAnat.m b/tests/test_setBatchSelectAnat.m
new file mode 100644
index 00000000..e374ea04
--- /dev/null
+++ b/tests/test_setBatchSelectAnat.m
@@ -0,0 +1,42 @@
+function test_suite = test_setBatchSelectAnat %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSelectAnatBasic()
+
+ % TODO
+ % add test to check if anat is not in first session
+ % add test to check if anat is not a T1w
+
+ opt.dataDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demos', ...
+ 'MoAE', 'output', 'MoAEpilot');
+ opt.taskName = 'auditory';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ subID = '01';
+
+ matlabbatch = [];
+ matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subID);
+
+ expectedBatch{1}.cfg_basicio.cfg_named_file.name = 'Anatomical';
+
+ anatFile = spm_select('FPlist', ...
+ fullfile( ...
+ opt.dataDir, '..', ...
+ 'derivatives', ...
+ 'SPM12_CPPL', ...
+ 'sub-01', ...
+ 'anat'), ...
+ 'sub-01_T1w.nii');
+ expectedBatch{1}.cfg_basicio.cfg_named_file.files = { {anatFile} };
+
+ assertEqual(matlabbatch, expectedBatch);
+
+end
diff --git a/tests/test_setBatchSkullStripping.m b/tests/test_setBatchSkullStripping.m
new file mode 100644
index 00000000..cd4005a1
--- /dev/null
+++ b/tests/test_setBatchSkullStripping.m
@@ -0,0 +1,105 @@
+function test_suite = test_setBatchSkullStripping %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSkullStrippingBasic()
+
+ subID = '01';
+
+ opt.taskName = 'vislocalizer';
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.groups = {''};
+ opt.subjects = {subID};
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ opt.orderBatches.segment = 2;
+
+ matlabbatch = [];
+ matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, subID, opt);
+
+ expectedBatch = returnExpectedBatch(opt);
+
+ assertEqual(matlabbatch, expectedBatch);
+
+end
+
+function expectedBatch = returnExpectedBatch(opt)
+
+ expectedFileName = 'sub-01_ses-01_T1w.nii';
+
+ expectedAnatDataDir = fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'derivatives', 'SPM12_CPPL', ...
+ 'sub-01', 'ses-01', 'anat');
+
+ expectedBatch = [];
+ expectedBatch{end + 1}.spm.util.imcalc.input(1) = ...
+ cfg_dep( ...
+ 'Segment: Bias Corrected (1)', ...
+ substruct( ...
+ '.', 'val', '{}', {2}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'channel', '()', {1}, ...
+ '.', 'biascorr', '()', {':'}));
+
+ expectedBatch{end}.spm.util.imcalc.input(2) = ...
+ cfg_dep( ...
+ 'Segment: c1 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {2}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {1}, ...
+ '.', 'c', '()', {':'}));
+
+ expectedBatch{end}.spm.util.imcalc.input(3) = ...
+ cfg_dep( ...
+ 'Segment: c2 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {2}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {2}, ...
+ '.', 'c', '()', {':'}));
+ expectedBatch{end}.spm.util.imcalc.input(4) = ...
+ cfg_dep( ...
+ 'Segment: c3 Images', ...
+ substruct( ...
+ '.', 'val', '{}', {2}, ...
+ '.', 'val', '{}', {1}, ...
+ '.', 'val', '{}', {1}), ...
+ substruct( ...
+ '.', 'tiss', '()', {3}, ...
+ '.', 'c', '()', {':'}));
+
+ expectedBatch{end}.spm.util.imcalc.output = ['m' strrep( ...
+ expectedFileName, ...
+ '.nii', ...
+ '_skullstripped.nii')];
+ expectedBatch{end}.spm.util.imcalc.outdir = {expectedAnatDataDir};
+
+ expectedBatch{end}.spm.util.imcalc.expression = sprintf( ...
+ 'i1.*((i2+i3+i4)>%f)', ...
+ opt.skullstrip.threshold);
+
+ % add a batch to output the mask
+ expectedBatch{end + 1} = expectedBatch{end};
+ expectedBatch{end}.spm.util.imcalc.expression = sprintf( ...
+ '(i2+i3+i4)>%f', ...
+ opt.skullstrip.threshold);
+ expectedBatch{end}.spm.util.imcalc.output = ['m' strrep( ...
+ expectedFileName, ...
+ '.nii', ...
+ '_mask.nii')];
+
+end
diff --git a/tests/test_setBatchSmoothing.m b/tests/test_setBatchSmoothing.m
new file mode 100644
index 00000000..abaec521
--- /dev/null
+++ b/tests/test_setBatchSmoothing.m
@@ -0,0 +1,51 @@
+function test_suite = test_setBatchSmoothing %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSmoothingBasic()
+
+ % TODO
+ % need a test with several sessions and runs
+
+ subID = '01';
+
+ funcFWHM = 6;
+
+ opt.dataDir = fullfile(fileparts(mfilename('fullpath')), '..', 'demos', ...
+ 'MoAE', 'output', 'MoAEpilot');
+ opt.taskName = 'auditory';
+
+ opt = checkOptions(opt);
+
+ [~, opt, BIDS] = getData(opt);
+
+ % create dummy normalized file
+ fileName = spm_BIDS(BIDS, 'data', ...
+ 'sub', subID, ...
+ 'task', opt.taskName, ...
+ 'type', 'bold');
+ [filepath, filename, ext] = fileparts(fileName{1});
+ fileName = fullfile( ...
+ filepath, ...
+ [spm_get_defaults('normalise.write.prefix') filename ext]);
+ system(sprintf('touch %s', fileName));
+
+ matlabbatch = [];
+ matlabbatch = setBatchSmoothing(BIDS, opt, subID, funcFWHM);
+
+ expectedBatch{1}.spm.spatial.smooth.fwhm = [6 6 6];
+ expectedBatch{1}.spm.spatial.smooth.dtype = 0;
+ expectedBatch{1}.spm.spatial.smooth.im = 0;
+ expectedBatch{1}.spm.spatial.smooth.prefix = ...
+ [spm_get_defaults('smooth.prefix'), '6'];
+ expectedBatch{1}.spm.spatial.smooth.data{1} = fileName;
+
+ assertEqual( ...
+ matlabbatch{1}.spm.spatial.smooth, ...
+ expectedBatch{1}.spm.spatial.smooth);
+
+end
diff --git a/tests/test_setBatchSubjectLevelContrasts.m b/tests/test_setBatchSubjectLevelContrasts.m
new file mode 100644
index 00000000..511e879e
--- /dev/null
+++ b/tests/test_setBatchSubjectLevelContrasts.m
@@ -0,0 +1,44 @@
+function test_suite = test_setBatchSubjectLevelContrasts %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSubjectLevelContrastsBasic()
+
+ funcFWHM = 6;
+ subID = '01';
+ iSes = 1;
+ iRun = 1;
+
+ % directory with this script becomes the current directory
+ opt.subjects = {subID};
+ opt.taskName = 'auditory';
+ opt.dataDir = fullfile( ...
+ fileparts(mfilename('fullpath')), ...
+ '..', 'demos', 'MoAE', 'output', 'MoAEpilot');
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ '..', 'demos', 'MoAE', 'models', 'model-MoAE_smdl.json');
+
+ opt = checkOptions(opt);
+
+ ffxDir = fullfile(opt.derivativesDir, 'sub-01', 'stats', 'ffx_auditory', ...
+ 'ffx_space-MNI_FWHM-6');
+
+ bidsCopyRawFolder(opt, 1);
+
+ % matlabbatch = setBatchSubjectLevelContrasts(opt, subID, funcFWHM);
+
+ % TODO add assert
+ % expectedBatch = returnExpectedBatch();
+ % assert(matlabbatch, returnExpectedBatch);
+
+end
+
+% function expectedBatch = returnExpectedBatch()
+%
+%
+%
+% end
diff --git a/tests/test_setBatchSubjectLevelGLMSpec.m b/tests/test_setBatchSubjectLevelGLMSpec.m
new file mode 100644
index 00000000..cc693e1a
--- /dev/null
+++ b/tests/test_setBatchSubjectLevelGLMSpec.m
@@ -0,0 +1,97 @@
+function test_suite = test_setBatchSubjectLevelGLMSpec %#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
+ end
+ initTestSuite;
+end
+
+function test_setBatchSubjectLevelGLMSpecBasic()
+
+ funcFWHM = 6;
+ subID = '01';
+ iSes = 1;
+ iRun = 1;
+
+ opt.subjects = {subID};
+ opt.taskName = 'auditory';
+ opt.dataDir = fullfile( ...
+ fileparts(mfilename('fullpath')), ...
+ '..', 'demos', 'MoAE', 'output', 'MoAEpilot');
+ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ...
+ '..', 'demos', 'MoAE', 'models', 'model-MoAE_smdl.json');
+ opt = checkOptions(opt);
+
+ bidsCopyRawFolder(opt, 1);
+
+ [~, opt, BIDS] = getData(opt);
+
+ % create dummy preprocessed data
+ sessions = getInfo(BIDS, subID, opt, 'Sessions');
+ runs = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes});
+ [fileName, subFuncDataDir] = getBoldFilename( ...
+ BIDS, ...
+ subID, sessions{iSes}, runs{iRun}, opt);
+ copyfile(fullfile(subFuncDataDir, fileName), ...
+ fullfile(subFuncDataDir, ['s6w', fileName]));
+
+ % create dummy realign parameter file
+ system(sprintf('touch %s', ...
+ fullfile(subFuncDataDir, ['rp_', strrep(fileName, '.nii', '.txt')])));
+
+ matlabbatch = setBatchSubjectLevelGLMSpec(BIDS, opt, subID, funcFWHM);
+
+ % TODO add assert
+ % expectedBatch = returnExpectedBatch();
+ % assert(matlabbatch, returnExpectedBatch);
+
+end
+
+% function expectedBatch = returnExpectedBatch()
+%
+% matlabbatch{1}.spm.stats.fmri_spec.dir = { outputDir};
+%
+% matlabbatch{1}.spm.stats.fmri_spec.timing.RT = RT;
+% matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t = nbSlices;
+% matlabbatch{1}.spm.stats.fmri_spec.timing.fmri_t0 = midSlice;
+%
+% for iRun = 1:nbRuns
+%
+% matlabbatch{1}.spm.stats.fmri_spec.sess(iRun).scans = { boldFileForFFX};
+% matlabbatch{1}.spm.stats.fmri_spec.sess(end).multi = { eventFile };
+% matlabbatch{1}.spm.stats.fmri_spec.sess(end).multi_reg = { confoundsFile };
+%
+% % Things that are unlikely to change
+% matlabbatch{1}.spm.stats.fmri_spec.sess(end).hpf = 128;
+% matlabbatch{1}.spm.stats.fmri_spec.sess(end).regress = struct( ...
+% 'name', {}, ...
+% 'val', {});
+% matlabbatch{1}.spm.stats.fmri_spec.sess(end).cond = struct( ...
+% 'name', {}, ...
+% 'onset', {}, ...
+% 'duration', {}, ...
+% 'tmod', {}, ...
+% 'pmod', {}, ...
+% 'orth', {});
+% end
+%
+% % Things that may change
+% matlabbatch{1}.spm.stats.fmri_spec.bases.hrf.derivs = [0 0];
+%
+% matlabbatch{1}.spm.stats.fmri_spec.mthresh = 0.8;
+%
+% matlabbatch{1}.spm.stats.fmri_spec.mask = {''};
+%
+% matlabbatch{1}.spm.stats.fmri_spec.cvi = 'FAST';
+%
+% % Things that are unlikely to change
+% matlabbatch{1}.spm.stats.fmri_spec.timing.units = 'secs';
+%
+% matlabbatch{1}.spm.stats.fmri_spec.fact = struct( ...
+% 'name', {}, ...
+% 'levels', {});
+%
+% matlabbatch{1}.spm.stats.fmri_spec.volt = 1;
+% matlabbatch{1}.spm.stats.fmri_spec.global = 'None';
+%
+% end
diff --git a/tests/test_setDefaultFields.m b/tests/test_setDefaultFields.m
index 39ca05d4..548101ae 100644
--- a/tests/test_setDefaultFields.m
+++ b/tests/test_setDefaultFields.m
@@ -1,43 +1,68 @@
function test_suite = test_setDefaultFields %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_setDefaultFieldsWrite()
- %% set up
- structure = struct();
+ %% set up
+ structure = struct();
- fieldsToSet.field = 1;
+ fieldsToSet.field = 1;
- structure = setDefaultFields(structure, fieldsToSet);
+ structure = setDefaultFields(structure, fieldsToSet);
- %% data to test against
- expectedStructure.field = 1;
+ %% data to test against
+ expectedStructure.field = 1;
- %% test
- assertEqual(expectedStructure, structure);
+ %% test
+ assertEqual(expectedStructure, structure);
end
function test_setDefaultFieldsNoOverwrite()
- % set up
- structure.field.subfield_1 = 3;
+ % set up
+ structure.field.subfield_1 = 3;
- fieldsToSet.field.subfield_1 = 1;
- fieldsToSet.field.subfield_2 = 1;
+ fieldsToSet.field.subfield_1 = 1;
+ fieldsToSet.field.subfield_2 = 1;
- structure = setDefaultFields(structure, fieldsToSet);
+ structure = setDefaultFields(structure, fieldsToSet);
- % data to test against
- expectedStructure.field.subfield_1 = 3;
- expectedStructure.field.subfield_2 = 1;
+ % data to test against
+ expectedStructure.field.subfield_1 = 3;
+ expectedStructure.field.subfield_2 = 1;
- % test
- assert(isequal(expectedStructure, structure));
+ % test
+ assert(isequal(expectedStructure, structure));
+
+end
+
+function test_setDefaultFieldsCmplxStruct()
+
+ % set up
+ structure = struct();
+
+ fieldsToSet.field.subfield_1 = 1;
+ fieldsToSet.field.subfield_2(1).name = 'a';
+ fieldsToSet.field.subfield_2(1).value = 1;
+ fieldsToSet.field.subfield_2(2).name = 'b';
+ fieldsToSet.field.subfield_2(2).value = 2;
+
+ structure = setDefaultFields(structure, fieldsToSet);
+
+ % data to test against
+ expectedStructure.field.subfield_1 = 1;
+ expectedStructure.field.subfield_2(1).name = 'a';
+ expectedStructure.field.subfield_2(1).value = 1;
+ expectedStructure.field.subfield_2(2).name = 'b';
+ expectedStructure.field.subfield_2(2).value = 2;
+
+ % test
+ assert(isequal(expectedStructure, structure));
end
diff --git a/tests/test_setDerivativesDir.m b/tests/test_setDerivativesDir.m
new file mode 100644
index 00000000..4ee4a6b3
--- /dev/null
+++ b/tests/test_setDerivativesDir.m
@@ -0,0 +1,27 @@
+function test_suite = test_setDerivativesDir %#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
+ end
+ initTestSuite;
+end
+
+function test_setDerivativesDirBasic()
+
+ opt.dataDir = pwd;
+ opt.taskName = 'testTask';
+ opt = setDerivativesDir(opt);
+
+ assertEqual(opt.derivativesDir, fullfile(pwd, '..', 'derivatives', 'SPM12_CPPL'));
+
+end
+
+function test_setDerivativesDirMissing()
+
+ opt.derivativesDir = pwd;
+ opt.taskName = 'testTask';
+ opt = setDerivativesDir(opt);
+
+ assertEqual(opt.derivativesDir, fullfile(pwd, 'derivatives', 'SPM12_CPPL'));
+
+end
diff --git a/tests/test_specifyContrasts.m b/tests/test_specifyContrasts.m
index a707143f..f31380a4 100644
--- a/tests/test_specifyContrasts.m
+++ b/tests/test_specifyContrasts.m
@@ -1,36 +1,37 @@
function test_suite = test_specifyContrasts %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_specifyContrastsBasic()
- % Small test to ensure that pmCon returns what we asked for
+ % Small test to ensure that pmCon returns what we asked for
- opt.dataDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives');
- opt.taskName = 'visMotion';
- opt.model.univariate.file = ...
- fullfile(fileparts(mfilename('fullpath')), ...
- 'dummyData', 'model', 'model-visMotionLoc_smdl.json');
+ opt.derivativesDir = fullfile(fileparts(mfilename('fullpath')), 'dummyData');
+ opt.taskName = 'visMotion';
+ opt.model.file = ...
+ fullfile(fileparts(mfilename('fullpath')), ...
+ 'dummyData', 'models', 'model-visMotionLoc_smdl.json');
- ffxDir = fullfile(opt.dataDir, 'SPM12_CPPL', 'sub-01', 'stats', 'ffx_visMotion', 'ffx_6');
+ opt = setDerivativesDir(opt);
- isMVPA = 0;
+ ffxDir = fullfile(opt.derivativesDir, 'sub-01', 'stats', 'ffx_visMotion', ...
+ 'ffx_space-MNI_FWHM-6');
- contrasts = specifyContrasts(ffxDir, opt.taskName, opt, isMVPA);
+ contrasts = specifyContrasts(ffxDir, opt.taskName, opt);
- assert(strcmp(contrasts(1).name, 'VisMot'));
- assert(isequal(contrasts(1).C, [1 0 0 0 0 0 0 0 0]));
+ assert(strcmp(contrasts(1).name, 'VisMot'));
+ assert(isequal(contrasts(1).C, [1 0 0 0 0 0 0 0 0]));
- assert(strcmp(contrasts(2).name, 'VisStat'));
- assert(isequal(contrasts(2).C, [0 1 0 0 0 0 0 0 0]));
+ assert(strcmp(contrasts(2).name, 'VisStat'));
+ assert(isequal(contrasts(2).C, [0 1 0 0 0 0 0 0 0]));
- assert(strcmp(contrasts(3).name, 'VisMot_gt_VisStat'));
- assert(isequal(contrasts(3).C, [1 -1 0 0 0 0 0 0 0]));
+ assert(strcmp(contrasts(3).name, 'VisMot_gt_VisStat'));
+ assert(isequal(contrasts(3).C, [1 -1 0 0 0 0 0 0 0]));
- assert(strcmp(contrasts(4).name, 'VisStat_gt_VisMot'));
- assert(isequal(contrasts(4).C, [-1 1 0 0 0 0 0 0 0]));
+ assert(strcmp(contrasts(4).name, 'VisStat_gt_VisMot'));
+ assert(isequal(contrasts(4).C, [-1 1 0 0 0 0 0 0 0]));
end
diff --git a/tests/test_utils.m b/tests/test_utils.m
index 4f283dd1..4418b9cc 100644
--- a/tests/test_utils.m
+++ b/tests/test_utils.m
@@ -1,15 +1,15 @@
function test_suite = test_utils %#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
- end
- initTestSuite;
+ try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+ test_functions = localfunctions(); %#ok<*NASGU>
+ catch % no problem; early Matlab versions can use initTestSuite fine
+ end
+ initTestSuite;
end
function test_utilsBasic()
- printCredits();
+ printCredits();
- checkDependencies();
+ checkDependencies();
end
diff --git a/tests/test_validationInputFile.m b/tests/test_validationInputFile.m
new file mode 100644
index 00000000..2ff1ca98
--- /dev/null
+++ b/tests/test_validationInputFile.m
@@ -0,0 +1,36 @@
+function test_suite = test_validationInputFile %#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
+ end
+ initTestSuite;
+end
+
+function test_validationInputFileBasic()
+
+ directory = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
+ 'SPM12_CPPL', 'sub-01', 'ses-01', 'func');
+ prefix = '';
+ fileName = 'sub-01_ses-01_task-vislocalizer_bold.nii';
+
+ expectedOutput = fullfile(fileparts(mfilename('fullpath')), 'dummyData', 'derivatives', ...
+ 'SPM12_CPPL', 'sub-01', 'ses-01', 'func', ...
+ 'sub-01_ses-01_task-vislocalizer_bold.nii');
+
+ file = validationInputFile(directory, fileName, prefix);
+
+ assertEqual(expectedOutput, file);
+
+end
+
+function test_validationInputFileError()
+
+ directory = pwd;
+ prefix = 'swa';
+ fileName = 'gibberish.nii.gz';
+
+ assertExceptionThrown( ...
+ @()validationInputFile(directory, prefix, fileName), ...
+ 'validationInputFile:nonExistentFile');
+
+end
diff --git a/version.txt b/version.txt
index 8ce995b8..9ff151c5 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-v0.0.3
\ No newline at end of file
+v0.1.0
\ No newline at end of file