Repository: Deep-MI/fsqc (language: python)

Issue:
Merge freesurfer-module-releases into dev
Merges changes from the former 'stable' branch to current dev.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml
new file mode 100644
index 0000000..efa1aa0
--- /dev/null
+++ b/.github/workflows/code-style.yml
@@ -0,0 +1,24 @@
+name: code-style
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }}
+  cancel-in-progress: true
+on:
+  workflow_dispatch:
+jobs:
+  style:
+    timeout-minutes: 10
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+      - name: Setup Python 3.9
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.9'
+          architecture: 'x64'
+      - name: Install dependencies
+        run: |
+          python -m pip install --progress-bar off --upgrade pip setuptools wheel
+          python -m pip install --progress-bar off .[style]
+      - name: Run flake8
+        run: flake8
diff --git a/.gitignore b/.gitignore
index 39c9bca..4526199 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,40 @@
-__pycache__
-*.pyc
-TODO.md
+#
+misc
 deprecated
-package
-.idea
+testing
+
+# macOS
+.DS_Store
+
+#
+.flake8
+
+#
+.ipynb_checkpoints
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..7f2834f
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,17 @@
+# Changelog
+
+This is a document summarizing the changes that are associated with (major) updates and releases. Priority is given to changes that are relevant to the user, and those that introduce new features or break compatibility with prior versions.
+
+## Version 2.0
+
+- Name changes for the toolbox, repository, package, and scripts: the toolbox has been renamed to "fsqc tools", the python package name "qatoolspython" has been changed to "fsqc", and the "qatools.py" script has been renamed to "run_fsqc". The github repository is now located at "https://github.com/Deep-MI/fsqc", but requests to the old "https://github.com/Deep-MI/qatools-python" repository will be redirected.
+- Name changes for output files (replacing 'qatools' with 'fsqc').
+- Interface changes: importing and running the scripts as a python package has changed. See [here](https://github.com/Deep-MI/fsqc) for details. In particular, 'run_fsqc' is now an executable file and replaces the 'qatools' or 'qatools.py' command.
+- New names for main and dev branches: the main (default) branch is now called `stable` (was: `freesurfer-module-releases`), and the dev branch is now called `dev` (was: `freesurfer-module-dev`). The old branches will still be kept for a while in a deprecation state, but will not recieve further updates or support. 
+- Screenshots are now in radiological orientation (left is right). Before version 2.0, screenshots were in neurological orientation (left is left).
+- Addition of skullstrip, hippocampus and hypothalamus modules.
+- Improved logging, error handling, and testing frameworks.
+- Enhanced containerization support (docker, singularity, dockerhub).
+- Updated requirements, easier installation of lapy and brainprint dependencies.
+- FreeSurfer is no longer required as a dependency (except for the optional 'shape analysis' module, which relies on the brainprint package).
+- We do not preserve compatibility with earlier versions.
diff --git a/README.md b/README.md
index e14b81f..39094e0 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,38 @@
-# qatools-python
+# fsqc
+
+## Contents
+
+- [Description](#description)
+- [Ongoing development](#ongoing-development)
+  - [Recent changes](#recent-changes)
+  - [Main and development branches](#main-and-development-branches)
+  - [Roadmap](#roadmap)
+- [Usage](#usage)
+  - [As a command line tool](#as-a-command-line-tool)
+  - [As a Python package](#as-a-python-package)
+  - [As a Docker image](#as-a-docker-image)
+- [Installation](#installation)
+   - [Installation as a Python package](#installation-as-a-python-package)
+   - [Installation from GitHub](#installation-from-github)
+   - [Download from GitHub](#download-from-github)
+- [Requirements](#requirements)
+- [Known issues](#known-issues)
+- [Authors](#authors)
+- [Citations](#citations)
+- [License](#license)
 
 ___
 
 ## Description
 
-This is a set of quality assurance / quality control scripts for Freesurfer
-processed structural MRI data.
+This is a set of quality assurance / quality control scripts for Fastsurfer- or
+Freesurfer-processed structural MRI data.
 
 It is a revision, extension, and translation to the Python language of the
-Freesurfer QA Tools that are provided at https://surfer.nmr.mgh.harvard.edu/fswiki/QATools
-
-It has been augmented by additional functions from the MRIQC toolbox, available
-at https://github.com/poldracklab/mriqc and https://osf.io/haf97, and with code
-derived from the shapeDNA and brainPrint toolboxes, available at
-https://reuter.mit.edu.
+[Freesurfer QA Tools](https://surfer.nmr.mgh.harvard.edu/fswiki/QATools). It has
+been augmented by additional functions from the [MRIQC toolbox](https://github.com/poldracklab/mriqc),
+and with code derived from the [lapy](https://github.com/Deep-MI/lapy) and
+[brainPrint](https://github.com/Deep-MI/brainprint) toolboxes.
 
 The core functionality of this toolbox is to compute the following features:
 
@@ -42,10 +61,10 @@ write a csv table into that location. The csv table will contain the above
 metrics plus a subject identifier.
 
 The program can also be run on images that were processed with [FastSurfer](https://github.com/Deep-MI/FastSurfer)
-instead of FreeSurfer. In that case, simply add a `--fastsurfer` switch to your
-shell command. Note that the full processing stream FastSurfer must have been
-run, including surface reconstruction (i.e. brain segmentation alone is not
-sufficient). FastSurfer v1.1 or later is required.
+(v1.1 or later) instead of FreeSurfer. In that case, simply add a `--fastsurfer`
+switch to your shell command. Note that FastSurfer's full processing stream must
+have been run, including surface reconstruction (i.e. brain segmentation alone
+is not sufficient).
 
 In addition to the core functionality of the toolbox there are several optional
 modules that can be run according to need:
@@ -59,17 +78,42 @@ that will be created within the output directory. These images can be used for
 quickly glimpsing through the processing results. Note that no display manager
 is required for this module, i.e. it can be run on a remote server, for example.
 
+- surfaces module
+
+This module allows for the automated generation of surface renderings of the
+left and right pial and inflated surfaces, overlaid with the aparc annotation.
+These images will be saved to the 'surfaces' subdirectory that will be created
+within the output directory. These images can be used for quickly glimpsing
+through the processing results. Note that no display manager is required for
+this module, i.e. it can be run on a remote server, for example.
+
+- skullstrip module
+
+This module allows for the automated generation cross-sections of the brain
+that are overlaid with the colored and semi-transparent brainmask. This allows
+to check the quality of the skullstripping in FreeSurfer. The resulting images
+will be saved to the 'skullstrip' subdirectory that will be created within the
+output directory.
+
 - fornix module
 
-This is a module to assess potential issues with the segementation of the
+This is a module to assess potential issues with the segmentation of the
 corpus callosum, which may incorrectly include parts of the fornix. To assess
 segmentation quality, a screenshot of the contours of the corpus callosum
 segmentation overlaid on the norm.mgz will be saved as 'cc.png' for each
 subject within the 'fornix' subdirectory of the output directory.
 
+- modules for the amygdala, hippocampus, and hypothalamus
+
+These modules evaluate potential missegmentations of the amygdala, hippocampus,
+and hypothalamus. To assess segmentation quality, screenshots will be created
+These modules require prior processing of the MR images with FreeSurfer's
+dedicated toolboxes for the segmentation of the amygdala and hippocampus, and
+the hypothalamus, respectively.
+
 - shape module
 
-The shape module will run a shapeDNA / braiprint analysis to compute distances
+The shape module will run a shapeDNA / brainprint analysis to compute distances
 of shape descriptors between lateralized brain structures. This can be used
 to identify discrepancies and irregularities between pairs of corresponding
 structures. The results will be included in the main csv table, and the output
@@ -78,7 +122,10 @@ directory will also contain a 'brainprint' subdirectory.
 - outlier module
 
 This is a module to detect extreme values among the subcortical ('aseg')
-segmentations. The outlier detection is based on comparisons with the
+segmentations as well as the cortical parcellations. If present, hypothalamic
+and hippocampal subsegmentations will also be included.
+
+The outlier detection is based on comparisons with the
 distributions of the sample as well as normative values taken from the
 literature (see References).
 
@@ -96,8 +143,10 @@ alternative, users may specify their own normative values by using the
 '--outlier-table' argument. This requires a custom csv table with headers
 `label`, `upper`, and `lower`, where `label` indicates a column of anatomical
 names. It can be a subset and the order is arbitrary, but naming must exactly
-match the nomenclature of the 'aseg.stats' file. `upper` and `lower` are user-
-specified upper and lower bounds.
+match the nomenclature of the 'aseg.stats' and/or '[lr]h.aparc.stats' file.
+If cortical parcellations are included in the outlier table for a comparison
+with aparc.stats values, the labels must have a 'lh.' or 'rh.' prefix. file.
+`upper` and `lower` are user-specified upper and lower bounds.
 
 The main csv table will be appended with the following summary variables, and
 more detailed output about will be saved as csv tables in the 'outliers'
@@ -111,20 +160,74 @@ n_outliers_norms         | number of structures exceeding the upper and lower bo
 
 ___
 
+## Ongoing development
+
+### Recent changes
+
+We are happy to announce the release of version 2.0 of the fsqc toolbox. With
+this release comes a change of the project name from `qatools` to `fsqc`, to
+reflect increased independence from the original FreeSurfer QA tools, and
+applicability to other neuroimaging analysis packages - such as [Fastsurfer](https://github.com/Deep-MI/FastSurfer).
+
+The current version of this toolbox is v2.0; recent changes include the addition
+of the skullstrip, hipopcampus and hypothalamus modules as well as the addition
+of a surface visualization module. Technical changes include how the package is
+installed, imported, and run, see below for details.
+
+A list of changes is available [here](CHANGES.md).
+
+### Main and development branches
+
+This repository contains multiple branches, reflecting the continued
+development of the toolbox. The two primary branches are the main branch
+(`stable`) and the development branch (`dev`). New features will first be added
+to the development branch, and eventually be merged with the main branch. You
+are currently on the development branch. To go to the main branch, select it
+from the drop-down menu on the top left, or [click here](https://github.com/Deep-MI/fsqc/tree/stable).
+
+### Roadmap
+
+The goal of the `fsqc` project is to create a modular and extensible
+software package that provides quantitative metrics and visual information for
+the quality control of FreeSurfer- or Fastsurfer-processed MR images. The
+package is currently under development, and new features are continuously
+added.
+
+New features will initially be available in the [development branch](https://github.com/Deep-MI/fsqc/tree/dev)
+of this toolbox and will be included in the [main branch](https://github.com/Deep-MI/fsqc/tree/stable)
+after a period of testing and evaluation. Unless explicitly announced, all new
+features will preserve compatibility with earlier versions.
+
+Upcoming extensions include modules for the quality control of FreeSurfer's
+brainstem and thalamic segmentations. Another planned extension is support for
+parallel processing of many cases.
+
+Feedback, suggestions, and contributions are always welcome, preferably via
+[issues](https://github.com/Deep-MI/fsqc/issues) and [pull requests](https://github.com/Deep-MI/fsqc/pulls).
+
+___
+
 ## Usage
 
+### As a command line tool
+
 ```
-python3 qatools.py --subjects_dir <directory> --output_dir <directory>
-                          [--subjects SubjectID [SubjectID ...]]
-                          [--subjects-file <file>] [--screenshots]
-                          [--screenshots-html] [--fornix] [--fornix-html]
-                          [--shape] [--outlier] [--fastsurfer] [-h]
+run_fsqc --subjects_dir <directory> --output_dir <directory>
+    [--subjects SubjectID [SubjectID ...]]
+    [--subjects-file <file>] [--screenshots]
+    [--screenshots-html] [--surfaces] [--surfaces-html]
+    [--skullstrip] [--skullstrip-html]
+    [--fornix] [--fornix-html] [--hippocampus]
+    [--hippocampus-html] [--hippocampus-label ... ]
+    [--hypothalamus] [--hypothalamus-html] [--shape]
+    [--outlier] [--fastsurfer] [-h] [--more-help]
+    [...]
 
 
 required arguments:
   --subjects_dir <directory>
-                         subjects directory with a set of Freesurfer 6.0 processed
-                         individual datasets.
+                         subjects directory with a set of Freesurfer- or
+                         Fastsurfer-processed individual datasets.
   --output_dir <directory>
                          output directory
 
@@ -132,160 +235,275 @@ optional arguments:
   --subjects SubjectID [SubjectID ...]
                          list of subject IDs
   --subjects-file <file> filename of a file with subject IDs (one per line)
-  --screenshots          create screenshots
-  --screenshots-html     create html summary page of screenshots (requires 
-                         --screenshots)
+  --screenshots          create screenshots of individual brains
+  --screenshots-html     create screenshots of individual brains incl.
+                         html summary page
+  --surfaces             create screenshots of individual brain surfaces
+  --surfaces-html        create screenshots of individual brain surfaces
+                         and html summary page
+  --skullstrip           create screenshots of individual brainmasks
+  --skullstrip-html      create screenshots of individual brainmasks and
+                         html summary page
   --fornix               check fornix segmentation
-  --fornix-html          create html summary page of fornix evaluation (requires 
-                         --fornix)
+  --fornix-html          check fornix segmentation and create html summary
+                         page of fornix evaluation
+  --hypothalamus         check hypothalamic segmentation
+  --hypothalamus-html    check hypothalamic segmentation and create html
+                         summary page
+  --hippocampus          check segmentation of hippocampus and amygdala
+  --hippocampus-html     check segmentation of hippocampus and amygdala
+                         and create html summary page
+  --hippocampus-label    specify label for hippocampus segmentation files
+                         (default: T1.v21). The full filename is then
+                         [lr]h.hippoAmygLabels-<LABEL>.FSvoxelSpace.mgz
   --shape                run shape analysis
   --outlier              run outlier detection
   --outlier-table        specify normative values (only in conjunction with
                          --outlier)
   --fastsurfer           use FastSurfer instead of FreeSurfer output
+  --exit-on-error        terminate the program when encountering an error;
+                         otherwise, try to continue with the next module or
+                         case
 
 getting help:
   -h, --help            display this help message and exit
+  --more-help           display extensive help message and exit
+
+expert options:
+  --screenshots_base <image>
+                        filename of an image that should be used instead of
+                        norm.mgz as the base image for the screenshots. Can be
+                        an individual file (which would not be appropriate for
+                        multi-subject analysis) or can be a file without
+                        pathname and with the same filename across subjects
+                        within the 'mri' subdirectory of an individual
+                        FreeSurfer results directory (which would be appropriate
+                        for multi-subject analysis).
+  --screenshots_overlay <image>
+                        path to an image that should be used instead of aseg.mgz
+                        as the overlay image for the screenshots; can also be
+                        none. Can be an individual file (which would not be
+                        appropriate for multi-subject analysis) or can be a file
+                        without pathname and with the same filename across
+                        subjects within the 'mri' subdirectory of an individual
+                        FreeSurfer results directory (which would be appropriate
+                        for multi-subject analysis).
+  --screenshots_surf <surf> [<surf> ...]
+                        one or more surface files that should be used instead
+                        of [lr]h.white and [lr]h.pial; can also be none. Can be
+                        one or more individual file(s) (which would not be
+                        appropriate for multi-subject analysis) or can be a
+                        (list of) file(s) without pathname and with the same
+                        filename across subjects within the 'surf' subdirectory
+                        of an individual FreeSurfer results directory (which
+                        would be appropriate for multi-subject analysis).
+  --screenshots_views <view> [<view> ...]
+                        one or more views to use for the screenshots in the form
+                        of x=<numeric> y=<numeric> and/or z=<numeric>. Order
+                        does not matter. Default views are x=-10 x=10 y=0 z=0.
+  --screenshots_layout <rows> <columns>
+                        layout matrix for screenshot images.
 
 ```
 
-___
-
-## Examples
+*Examples:*
 
 - Run the QC pipeline for all subjects found in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory
+```
 
 - Run the QC pipeline for two specific subjects that need to present in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --subjects mySubjectID1 mySubjectID2`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --subjects mySubjectID1 mySubjectID2
+```
 
 - Run the QC pipeline for all subjects found in `/my/subjects/directory` after full FastSurfer processing:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --fastsurfer`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --fastsurfer
+```
 
 - Run the QC pipeline plus the screenshots module for all subjects found in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --screenshots`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --screenshots
+```
 
 - Run the QC pipeline plus the fornix pipeline for all subjects found in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --fornix`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --fornix
+```
 
 - Run the QC pipeline plus the shape analysis pipeline for all subjects found in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --shape`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --shape
+```
 
 - Run the QC pipeline plus the outlier detection module for all subjects found in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --outlier`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --outlier
+```
 
 - Run the QC pipeline plus the outlier detection module with a user-specific table of normative values for all subjects found in `/my/subjects/directory`:
 
-    `python3 /my/scripts/directory/qatools.py --subjects_dir /my/subjects/directory --output_dir /my/output/directory --outlier --outlier-table /my/table/with/normative/values.csv`
+```bash
+run_fsqc --subjects_dir /my/subjects/directory --output_dir /my/output/directory --outlier --outlier-table /my/table/with/normative/values.csv
+```
 
 - Note that the `--screenshots`, `--fornix`, `--shape`, and `--outlier` arguments can also be used in conjunction.
 
+### As a Python package
+
+As an alternative to their command-line usage, the fsqc scripts can also be run
+within a pure Python environment, i.e. installed and imported as a Python package.
+
+Use `import fsqc` (or sth. equivalent) to import the package within a
+Python environment, and use the `run_fsqc` function from the `fsqc` module to
+run an analysis.
+
+In its most basic form:
+
+```python
+import fsqc
+fsqc.run_fsqc(subjects_dir='/my/subjects/dir', output_dir='/my/output/dir')
+```
+
+Specify subjects as a list:
+
+```python
+import fsqc
+fsqc.run_fsqc(subjects_dir='/my/subjects/dir', output_dir='/my/output/dir', subjects=['subject1', 'subject2', 'subject3'])
+```
+
+And as a more elaborate example:
+
+```python
+import fsqc
+fsqc.run_fsqc(subjects_dir='/my/subjects/dir', output_dir='/my/output/dir', subject_file='/my/subjects/file.txt', screenshots_html=True, surfaces_html=True, skullstrip_html=True, fornix_html=True, hypothalamus_html=True, hippocampus_html=True, hippocampus_label="T1.v21", shape=True, outlier=True)
+```
+
+
+Call `help(fsqc.run_fsqc)` for further usage info and additional options.
+
+### As a Docker image
+
+We provide a configuration files that can be used to create a Docker or
+Singularity image for the fsqc scripts. Documentation can be found on the
+[Docker](docker/Docker.md) and [Singularity](singularity/Singularity.md) pages.
+
 ___
 
 ## Main branch and development branch
 
-There are two branches in this repository, the main brach (`freesurfer-module-releases`) and the development branch (`freesurfer-module-dev`). New features will first be added to the development branch, and eventually be merged with the main branch. You are currently on the main branch. To go to the development branch, select it from the drop-down menu on the top left, or [click here](https://github.com/Deep-MI/qatools-python/tree/freesurfer-module-dev).
+There are two branches in this repository, the main brach (`stable`) and the
+development branch (`dev`). New features will first be added to the development
+branch, and eventually be merged with the main branch. You are currently on the
+development branch. To go to the main branch, select it from the drop-down menu
+on the top left, or [click here](https://github.com/Deep-MI/fsqc/tree/stable).
 
 ___
 
 ## Installation
 
-This software can be downloaded from its github repository at `https://github.com/Deep-MI/qatools-python`.
-
-Alternatively, it can be cloned directly from its repository via `git clone https://github.com/Deep-MI/qatools-python`.
+### Installation as a Python package
 
-The `qatools.py` script will then be executable from the command line, as
-detailed above.
+Use:
 
-Optional packages (if running the shape analysis module) include the `brainprint-python`
-and `lapy` packages from . They can be installed using
-`pip3 install --user git+https://github.com/Deep-MI/BrainPrint-python.git`
-and `pip3 install --user git+https://github.com/Deep-MI/LaPy.git`. They should
-both be version 0.2 or newer.
-___
+```bash
+pip install fsqc
+```
 
-## Use as a python package
+to install the fsqc package and all of its dependencies. This is the recommended
+way of installing the package, and allows for both command-line execution and
+execution as a Python function. We also recommend to do this installation within
+a Python virtual environment, which can be created and activated as follows:
 
-As an alternative to their command-line usage, the qc scripts can also be run
-within a pure python environment, i.e. installed and imported as a python
-package.
+```bash
+virtualenv /path/to/my/virtual/environment
+source /path/to/my/virtual/environment/bin/activate
+```
 
-Use the following code to download, build and install a package from its github
-repository into your local python package directory:
+### Installation from GitHub
 
-`pip3 install --user git+https://github.com/Deep-MI/qatools-python.git@freesurfer-module-releases`
+Use the following code to download, build and install the fsqc package from its
+GitHub repository into your local Python package directory:
 
-Use the following code to install the package in editable mode to a location of
-your choice:
+```bash
+pip install git+https://github.com/deep-mi/fsqc.git
+```
 
-`pip3 install --user --src /my/preferred/location --editable git+https://github.com/Deep-MI/qatools-python.git@freesurfer-module-releases`
+This can be useful if you want to install a particular branch - such as the `dev`
+branch in the following example:
 
-Use `import qatoolspython` (or sth. equivalent) to import the package within a
-python environment.
+```bash
+pip install git+https://github.com/deep-mi/fsqc.git@dev
+```
 
-Use the `run_qatools` function from the `qatoolspython` module to run an
-analysis:
+### Download from GitHub
 
-`from qatoolspython import qatoolspython`
+This software can also be downloaded from its GitHub repository at `https://github.com/Deep-MI/fsqc`,
+or cloned directly via `git clone https://github.com/Deep-MI/fsqc`.
 
-`qatoolspython.run_qatools(subjects_dir='/my/subjects/dir', output_dir='/my/output/dir')`
+The `run_fsqc` script will then be executable from the command line, as
+detailed above. Note, however, that the required dependencies will have to be
+installed manually. See the [requirements](#requirements) section for
+instructions.
 
-See `help(qatoolspython)` for further usage info and additional options.
 
 ___
 
-## Running from a Docker image
-
-We provide a `Dockerfile` that can be used to create a Docker image for the qatools-python scripts. Documentation is provided on the [Docker page](docker/Docker.md).
+## Requirements
 
-___
+- At least one structural MR image that was processed with Freesurfer 6.0, 7.x,
+  or FastSurfer 1.1 or later (including the surface pipeline).
 
-## Testing
+- A Python version >= 3.8 is required to run this script.
 
-Included with this toolbox is a `testing` subdirectory which contains two
-testing routines, `testing.sh` and `testing.py`. These can be used to test the
-installation and verify results.
+- Required packages include (among others) the nibabel and skimage package for
+  the core functionality, plus the matplotlib, pandas, and transform3d
+  packages for some optional functions and modules. See the `requirements.txt`
+  file for a complete list. Use `pip install -r requirements.txt` to install
+  these packages.
 
-These operate on a subset of the [Freesurfer tutorial data](https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/Data),
-which must be downloaded separately.
+- If installing the toolbox as a Python package or if using the Docker image,
+  all required packages will be installed automatically and manual installation
+  as detailed above will not be necessary.
 
-Extracting the downloaded file with `tar -xzvf tutorial_data.tar.gz` should
-give a `tutorial_data` directory. From `tutorial_data/buckner_data/tutorial_subjs/group_analysis_tutorial`,
-copy the following subjects to the `testing/data` subdirectory within the
-toolbox directory: `017`, `091`, `092`, `124`, `129`, `130`, `136`, and `145`.
+- This software has been tested on Ubuntu 20.04.
 
-Run the tests by executing `testing.sh` and/or `python3 testing.py` from within
-the `testing` subdirectory of this toolbox.
+- A working [FreeSurfer](https://freesurfer.net) installation (version 6 or 
+  newer) is required for running the 'shape' module of this toolbox. Also make 
+  sure that FreeSurfer is sourced (i.e., `FREESURFER_HOME` is set as an 
+  environment variable) before running an analysis.
 
 ___
 
-## Known Issues
+## Known issues
 
-- Aborted / restarted recon-all runs: the program will analyze recon-all 
-  logfiles, and may fail or return erroneous results if the logfile is 
-  append by multiple restarts of recon-all runs. Ideally, the logfile should
+- Aborted / restarted recon-all runs: the program will analyze recon-all
+  logfiles, and may fail or return erroneous results if the logfile is
+  appended by multiple restarts of recon-all runs. Ideally, the logfile should
   therefore consist of just a single, successful recon-all run.
-- High-resolution data: Prior to update v1.4, the screenshots and fornix module 
-  did not work well with high-resolution data that was processed using the 
-  -cm flag in recon-all. With update v1.4 this has been fixed for the 
-  screenhots module, but the fornix module is still experimental for 
+- High-resolution data: Prior to update v1.4, the screenshots and fornix module
+  did not work well with high-resolution data that was processed using the
+  -cm flag in recon-all. With update v1.4 this has been fixed for the
+  screenhots module, but the fornix module is still experimental for
   high-resolution data.
 
 ___
 
 ## Authors
 
-- qatools-python: Kersten Diers, Tobias Wolff, and Martin Reuter.
+- fsqc toolbox: Kersten Diers, Tobias Wolff, and Martin Reuter.
 - Freesurfer QA Tools: David Koh, Stephanie Lee, Jenni Pacheco, Vasanth Pappu,
   and Louis Vinke.
-- shapeDNA and brainPrint toolboxes: Martin Reuter.
+- lapy and brainprint toolboxes: Martin Reuter.
 
 ___
 
@@ -303,31 +521,9 @@ ___
   Eigenvalues and Topological Features of Eigenfunctions for Statistical Shape
   Analysis; Computer-Aided Design: 41, 739-755; doi:10.1016/j.cad.2009.02.007.
 
-- Potvin O, Mouiha A, Dieumegarde L, Duchesne S, & Alzheimer's Disease Neuroimaging
-  Initiative; 2016; Normative data for subcortical regional volumes over the lifetime
-  of the adult human brain; Neuroimage: 137, 9-20; doi.org/10.1016/j.neuroimage.2016.05.016
-
-___
-
-## Requirements
-
-- A working installation of Freesurfer (6.0 or 7.11) must be sourced.
-
-- At least one structural MR image that was processed with Freesurfer 6.0, 7.11,
-  or FastSurfer v1.1 or later (including the surface pipeline).
-
-- A Python version >= 3.5 is required to run this script.
-
-- Required packages include (among others) the nibabel and skimage package for
-  the core functionality, plus the matplotlib, pandas, and transform3d
-  packages for some optional functions and modules. See the `requirements.txt`
-  file for a complete list. Use `pip3 install -r requirements.txt` to install
-  these packages.
-
-- For the shape analysis module, the brainprint and lapy packages from https://github.com/Deep-MI
-  are required (both version 0.2 or newer).
-
-- This software has been tested on Ubuntu 20.04 and MacOS 10.15.
+- Potvin O, Mouiha A, Dieumegarde L, Duchesne S, & Alzheimer's Disease
+  Neuroimaging Initiative; 2016; Normative data for subcortical regional volumes
+  over the lifetime of the adult human brain; Neuroimage: 137, 9-20; doi.org/10.1016/j.neuroimage.2016.05.016
 
 ___
 
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..227cea2
--- /dev/null
+++ b/VERSION
@@ -0,0 +1,1 @@
+2.0.0
diff --git a/docker/Docker.md b/docker/Docker.md
index d4d22f9..8304f6c 100644
--- a/docker/Docker.md
+++ b/docker/Docker.md
@@ -1,37 +1,44 @@
-# QATools Docker
+# fsqc Docker
 
-Here we provide a [`Dockerfile`](Dockerfile) that can be used to create a Docker image and subsequently run the qatools scripts within a Docker container.
+Here we provide a [`Dockerfile`](Dockerfile) that can be used to create a Docker image and subsequently run the fsqc scripts within a Docker container. The main advantage of using virtualization technologies like Docker or Singularity is to create a controlled environment, with fixed versions of installable packages, which greatly helps with the stability and reproducibility of the analyses.
 
-The docker image will be based on Ubuntu 18.04, contain a Freesurfer 6.0 installation, the qatools scripts, the lapy and brainprint libraries, and any additionally required packages from the Ubuntu distribution.
+The docker image will be based on Ubuntu, contain the fsqc scripts, the lapy and brainprint libraries, and any additionally required packages from the Ubuntu distribution.
 
-The only thing that is required to run the qatools pipeline with all options is a valid FreeSurfer license (either from your local FreeSurfer installation or from the FreeSurfer website; https://surfer.nmr.mgh.harvard.edu/registration.html).
-
-## Build qatools Docker image
+## Build fsqc Docker image
 
 To build the docker image, execute the following command after traversing into the *docker* directory of this repository:
 
 ```bash
-docker build --rm -t qatoolsdocker -f Dockerfile .
+docker build --rm -t fsqcdocker -f Dockerfile .
 ```
-The name of the image will be `qatoolsdocker`, and it will be built from the `Dockerfile` configuration file from the *docker* directory.
+The name of the image will be `fsqcdocker`, and it will be built from the `Dockerfile` configuration file from the *docker* directory.
 
 The `--rm` flag will remove intermediate containers after a successful build; `-t` specifies the name of the image, and `-f` indicates the configuration file from which to build.
 
-Take a look at the contents of the [`Dockerfile`](Dockerfile) to see what is done during the build process: essentially, it is getting the Ubuntu 18.04 image, installing additional packages from the distribution, downloading and installing a copy of FreeSurfer 6.0 (without some of the very large image files and directories, which are not needed), downloading the qatools-python, brainprint-python, and lapy toolboxes, and setting the necessary environment variables. The resulting image will have a size of approximately 7 GB. Unless the `Dockerfile` changes, the build process has to be done only once.
+Take a look at the contents of the [`Dockerfile`](Dockerfile) to see what is done during the build process: essentially, it is getting the Ubuntu 22.04 image, installing additional packages from the distribution, downloading the fsqc toolbox, and setting the necessary environment variables. Unless the `Dockerfile` changes, the build process has to be done only once.
+
+## Download the Docker image
+
+As an alternative to building the Docker image yourself, you can also download our pre-built images from [Dockerhub](https://hub.docker.com/r/deepmi/fsqc/tags):
+
+```bash
+docker pull deepmi/fsqc:latest
+```
 
-## Run qatools from a Docker image
+## Run fsqc from a Docker image
 
-After building the qatoolsdocker image, run it with the following command to see the help message of the qatools-python main script:
+After building the fsqcdocker image, run it with the following command to see the help message of the fsqc main script:
 
 ```bash
-docker run --rm --user XXXX:YYYY qatoolsdocker
+docker run --rm --user XXXX:YYYY fsqcdocker
 ```
 
-* This corresponds to calling `python3 qatools.py` from the command line for a non-dockerized version of the program.
+* This corresponds to calling `python3 fsqc.py` from the command line for a non-dockerized version of the program.
 * The `--rm` flag takes care of removing the container once the analysis finished.
 * The `--user XXXX:YYYY` part should be changed to the appropriate user id (XXXX, a number) and group id (YYYY, also a number); both can be checked with the commands `id -u` and `id -g` on linux-like systems). All generated files will then belong to the specified user and group. Without the flag, the docker container will be run as root with all corresponding privileges, which is strongly discouraged.
+* You can run different versions of the image using `fsqc:<tag>` instead of `fsqc` and replacing `<tag>` with any particular version identifier.
 
-An actual analysis can be performed by adding several options to the above command (and after adjusting the user-specific settings and file- and pathnames):
+An analysis can be performed by adding several options to the above command (and after adjusting the user-specific settings and file- and pathnames):
 
 ```bash
 docker run \
@@ -39,14 +46,12 @@ docker run \
     --user XXXX:YYYY \
     -v /path/to/my/subjects/directory:/path_to_subjects_directory_inside_docker \
     -v /path/to/my/output/directory:/path_to_output_directory_inside_docker \
-    -v /path/to/my/freesurfer/license/file:/opt/freesurfer/.license:ro \
-    qatoolsdocker \
+    fsqcdocker \
     --subjects_dir /path_to_subjects_directory_inside_docker \
     --output_dir /path_to_output_directory_inside_docker
 ```
 
 * The first two `-v` arguments mount your data directory and output directories into the docker image. Inside the image, they are visible under the name following the colon (in this case `/data` and `/output`, but these can be different). From within the docker image / container, there will be read and write access to the directories that are mounted into the image (unless specified otherwise).
-* The third `-v` argument mounts your local FreeSurfer license file into the FreeSurfer directory within the docker image (`/opt/freesurfer`). The `:ro` suffix indicates that from within Docker, this will be read-only.
-* The next part of the docker command is the name of the Docker image, which is `qatoolsdocker`.
-* After that, all other flags are identical to the ones that are used for the `qatools.py` program (which are explained on the main page and the help message of the program). In addition to the `--subjects_dir` and `--output_dir` arguments, which are mandatory, the `--subjects`, `-screenshots`, `--fornix` arguments, for example, could be specified - in the same way as for non-dockerized version of the program. Note that file- and pathnames need to correspond to the targets of the file / directory mappings within the Docker image, not to the local system.
-* Also note that if you supply additional filenames to the qatools script (using e.g. the `--subjects-file` argument), their locations must be mounted using another `-v` option (unless they are present in one of the already mounted directories), and the filenames given to the script need to refer to the mounted location inside the Docker.
+* The next part of the docker command is the name of the Docker image, which is `fsqcdocker`.
+* After that, all other flags are identical to the ones that are used for the `fsqc.py` program (which are explained on the main page and the help message of the program). In addition to the `--subjects_dir` and `--output_dir` arguments, which are mandatory, the `--subjects`, `-screenshots`, `--fornix` arguments, for example, could be specified - in the same way as for non-dockerized version of the program. Note that file- and pathnames need to correspond to the targets of the file / directory mappings within the Docker image, not to the local system.
+* Also note that if you supply additional filenames to the fsqc script (using e.g. the `--subjects-file` argument), their locations must be mounted using another `-v` option (unless they are present in one of the already mounted directories), and the filenames given to the script need to refer to the mounted location inside the Docker.
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 3b20a8a..e85646a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,67 +1,39 @@
 # get OS
-FROM ubuntu:18.04
+FROM ubuntu:22.04
 
 # update OS
 RUN apt-get update
 
 # get additional packages
 RUN apt-get install -y --no-install-recommends \
-     ca-certificates \
-     wget \
-     tar \
-     zip \
-     man \
-     git \
-     gcc \
-     tcsh \
-     zlib1g-dev \
-     libjpeg-dev \
-     time \
-     python3 \
-     python3-dev \
-     python3-pip \
-     python3-setuptools \
-     python3-wheel
-
-# download FreeSurfer
-RUN wget -qO- https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/6.0.1/freesurfer-Linux-centos6_x86_64-stable-pub-v6.0.1.tar.gz | tar zxv --no-same-owner -C /opt \
-    --exclude='freesurfer/trctrain' \
-    --exclude='freesurfer/subjects/fsaverage_sym' \
-    --exclude='freesurfer/subjects/fsaverage3' \
-    --exclude='freesurfer/subjects/fsaverage4' \
-    --exclude='freesurfer/subjects/fsaverage5' \
-    --exclude='freesurfer/subjects/fsaverage6' \
-    --exclude='freesurfer/subjects/cvs_avg35' \
-    --exclude='freesurfer/subjects/cvs_avg35_inMNI152' \
-    --exclude='freesurfer/subjects/V1_average' \
-    --exclude='freesurfer/average/mult-comp-cor' \
-    --exclude='freesurfer/lib/cuda' \
-    --exclude='freesurfer/lib/qt'\
-    --exclude='freesurfer/MCRv80'
-
-# clone qatool-spython, brainprint-python, and lapy toolboxes
-RUN git clone https://github.com/Deep-MI/LaPy.git /app/lapy
-RUN git clone https://github.com/Deep-MI/BrainPrint-python.git /app/brainprint
-RUN git clone https://github.com/Deep-MI/qatools-python.git /app/qatools-python
-
-# install additional python packages
-#RUN pip3 install -r /app/qatools-python/requirements.txt
-RUN pip3 install `cat /app/qatools-python/requirements.txt | grep -v "importlib" | grep -v "#.*" `
-
-# Add FreeSurfer environment variables (.license file needed, alternatively export FS_LICENSE=path/to/license)
-ENV FREESURFER_HOME=/opt/freesurfer
+    gcc \
+    git \
+    time \
+    zlib1g-dev \
+    libjpeg-dev \
+    python3 \
+    python3-dev \
+    python3-pip \
+    python3-setuptools \
+    python3-wheel
+
+# clone fsqc
+#RUN git clone --branch dev --single-branch https://github.com/Deep-MI/fsqc.git /app/fsqc
+RUN git clone https://github.com/Deep-MI/fsqc.git /app/fsqc
+
+# install additional python packages (TODO: not necessary if using pip install)
+RUN pip3 install -r /app/fsqc/requirements.txt
 
 # Add other environment variables
 ENV OMP_NUM_THREADS=1
-
-# Set the PATH
-ENV PATH=/opt/freesurfer/bin:/opt/freesurfer/fsfast/bin:/opt/freesurfer/tktools:/opt/freesurfer/mni/bin:${PATH}
-
-# Add toolbox directories to PYTHONPATH
-ENV PYTHONPATH=/app/lapy:/app/brainprint:${PYTHONPATH}
+ENV MPLCONFIGDIR=/tmp/mplconfigdir
 
 # Set the working directory to /app
 WORKDIR /app
 
 # Set entrypoint (non-interactive mode)
-ENTRYPOINT ["python3", "/app/qatools-python/qatools.py"]
+ENTRYPOINT ["python3", "/app/fsqc/run_fsqc"]
+
+## Run app when the container launches (interactive mode)
+#CMD ["/bin/bash"] 
+
diff --git a/fsqc/__init__.py b/fsqc/__init__.py
new file mode 100644
index 0000000..06191b1
--- /dev/null
+++ b/fsqc/__init__.py
@@ -0,0 +1,1 @@
+from .fsqcMain import get_help, get_version, run_fsqc
diff --git a/qatoolspython/checkCCSize.py b/fsqc/checkCCSize.py
similarity index 60%
rename from qatoolspython/checkCCSize.py
rename to fsqc/checkCCSize.py
index da1594e..235c8d4 100644
--- a/qatoolspython/checkCCSize.py
+++ b/fsqc/checkCCSize.py
@@ -5,6 +5,7 @@
 
 # -----------------------------------------------------------------------------
 
+
 def checkCCSize(subjects_dir, subject):
     """
     A function to check the relative size of the corpus callosum.
@@ -21,29 +22,32 @@ def checkCCSize(subjects_dir, subject):
     Returns:
         - relative_cc
 
-    Requires a valid stats/aseg.stats file. If not found, NaNs are returned.
+    Requires a valid stats/aseg.stats file.
 
     """
 
     # Imports
     import os
+    import logging
     import numpy as np
 
     # Message
-    print('Checking size of the corpus callosum ...')
+    logging.info("Checking size of the corpus callosum ...")
 
-    # Check if files exist
-    path_stats_file = os.path.join(subjects_dir,subject,"stats","aseg.stats")
+    # Get file name and read contents
+    path_stats_file = os.path.join(subjects_dir, subject, "stats", "aseg.stats")
 
-    try:
-        with open(path_stats_file) as stats_file:
+    with open(path_stats_file) as stats_file:
             aseg_stats = stats_file.read().splitlines()
-    except FileNotFoundError:
-        print("WARNING: could not open "+path_stats_file+", returning NaNs.")
-        return np.nan
 
     # Initialize
-    cc_elements = ['CC_Posterior', 'CC_Mid_Posterior', 'CC_Central', 'CC_Mid_Anterior', 'CC_Anterior']
+    cc_elements = [
+        "CC_Posterior",
+        "CC_Mid_Posterior",
+        "CC_Central",
+        "CC_Mid_Anterior",
+        "CC_Anterior",
+    ]
 
     relative_cc = np.nan
 
@@ -56,12 +60,12 @@ def checkCCSize(subjects_dir, subject):
             # If the segmentation is found, compute the sum and return it.
             if cc_segmentation in aseg_stat_line:
                 sum_cc += float(aseg_stat_line.split()[3])
-            elif 'EstimatedTotalIntraCranialVol' in aseg_stat_line:
-                intracranial_volume= float(aseg_stat_line.split(',')[3])
+            elif "EstimatedTotalIntraCranialVol" in aseg_stat_line:
+                intracranial_volume = float(aseg_stat_line.split(",")[3])
 
-    relative_cc = sum_cc/intracranial_volume
+    relative_cc = sum_cc / intracranial_volume
 
-    print("Relative size of the corpus callosum is",'{:.4}'.format(relative_cc))
+    logging.info("Relative size of the corpus callosum is " + "{:.4}".format(relative_cc))
 
     # Return
     return relative_cc
diff --git a/qatoolspython/checkContrast.py b/fsqc/checkContrast.py
similarity index 53%
rename from qatoolspython/checkContrast.py
rename to fsqc/checkContrast.py
index 5389923..0f9d8f2 100644
--- a/qatoolspython/checkContrast.py
+++ b/fsqc/checkContrast.py
@@ -5,14 +5,15 @@
 
 # -----------------------------------------------------------------------------
 
+
 def checkContrast(subjects_dir, subject):
     """
     A function to compute the WM/GM contrast SNR.
 
-    This function computes the WM/GM contrast SNR based on the output of the 
-    pctsurfcon function. 
+    This function computes the WM/GM contrast SNR based on the output of the
+    pctsurfcon function.
 
-    Required arguments: 
+    Required arguments:
         - Subjects directory
         - Subject
 
@@ -20,64 +21,72 @@ def checkContrast(subjects_dir, subject):
         - con_lh_snr
         - con_rh_snr
 
-    Requires surf/[lr]h.w-g.pct.mgh and label/[lr]h.cortex.label. If not found, 
+    Requires surf/[lr]h.w-g.pct.mgh and label/[lr]h.cortex.label. If not found,
     NaNs will be returned.
 
     """
 
     # Imports
-    import os 
-    import sys
-    import numpy
+    import os
+    import logging
     import nibabel
-    from qatoolspython.qatoolspythonUtils import importMGH
+    import numpy
+    import warnings
+
+    from fsqc.fsqcUtils import importMGH
+
+    # Setttings
+    logging.captureWarnings(True)
 
     # Message
-    print('Checking WM/GM contrast SNR ...')
+    logging.info("Checking WM/GM contrast SNR ...")
 
     # Check if files exist
-    path_pct_lh = os.path.join(subjects_dir,subject,"surf","lh.w-g.pct.mgh")
+    path_pct_lh = os.path.join(subjects_dir, subject, "surf", "lh.w-g.pct.mgh")
     if not os.path.exists(path_pct_lh):
-        print("WARNING: could not find "+path_pct_lh+", returning NaNs")
+        warnings.warn("WARNING: could not find " + path_pct_lh + ", returning NaNs")
         return numpy.nan
 
-    path_pct_rh = os.path.join(subjects_dir,subject,"surf","rh.w-g.pct.mgh")
+    path_pct_rh = os.path.join(subjects_dir, subject, "surf", "rh.w-g.pct.mgh")
     if not os.path.exists(path_pct_rh):
-        print("WARNING: could not find "+path_pct_rh+", returning NaNs")
+        warnings.warn("WARNING: could not find " + path_pct_rh + ", returning NaNs")
         return numpy.nan
 
-    path_label_cortex_lh  = os.path.join(subjects_dir,subject,"label","lh.cortex.label")
+    path_label_cortex_lh = os.path.join(
+        subjects_dir, subject, "label", "lh.cortex.label"
+    )
     if not os.path.exists(path_label_cortex_lh):
-        print("WARNING: could not find "+path_label_cortex_lh+", returning NaNs")
+        warnings.warn("WARNING: could not find " + path_label_cortex_lh + ", returning NaNs")
         return numpy.nan
 
-    path_label_cortex_rh = os.path.join(subjects_dir,subject,"label","rh.cortex.label")
+    path_label_cortex_rh = os.path.join(
+        subjects_dir, subject, "label", "rh.cortex.label"
+    )
     if not os.path.exists(path_label_cortex_rh):
-        print("WARNING: could not find "+path_label_cortex_rh+", returning NaNs")
+        warnings.warn("WARNING: could not find " + path_label_cortex_rh + ", returning NaNs")
         return numpy.nan
-    
-    # Get the data fromt the mgh files
+
+    # Get the data from the mgh files
     con_lh = importMGH(path_pct_lh)
     con_rh = importMGH(path_pct_rh)
-    
+
     label_array_lh = nibabel.freesurfer.io.read_label(path_label_cortex_lh)
-    label_array_rh = nibabel.freesurfer.io.read_label(path_label_cortex_rh)    
-    
+    label_array_rh = nibabel.freesurfer.io.read_label(path_label_cortex_rh)
+
     # Only take the values of the cortex to compute the contrast control
     con_lh = numpy.take(con_lh, label_array_lh)
     con_rh = numpy.take(con_rh, label_array_rh)
-    
+
     # Compute the Contrast to noise ratio
     con_lh_mean = numpy.mean(con_lh)
     con_lh_std = numpy.std(con_lh)
-    con_lh_snr = con_lh_mean/con_lh_std
-    print("WM/GM contrast SNR for the left hemisphere:", '{:.4}'.format(con_lh_snr))
-    
+    con_lh_snr = con_lh_mean / con_lh_std
+    logging.info("WM/GM contrast SNR for the left hemisphere: " + "{:.4}".format(con_lh_snr))
+
     con_rh_mean = numpy.mean(con_rh)
     con_rh_std = numpy.std(con_rh)
-    con_rh_snr = con_rh_mean/con_rh_std 
-    print("WM/GM contrast SNR for the right hemisphere:", '{:.4}'.format(con_rh_snr))
-    
+    con_rh_snr = con_rh_mean / con_rh_std
+    logging.info("WM/GM contrast SNR for the right hemisphere: " + "{:.4}".format(con_rh_snr))
+
     # Return
     return con_lh_snr, con_rh_snr
-    
diff --git a/qatoolspython/checkRotation.py b/fsqc/checkRotation.py
similarity index 54%
rename from qatoolspython/checkRotation.py
rename to fsqc/checkRotation.py
index 8d25318..bc8f806 100644
--- a/qatoolspython/checkRotation.py
+++ b/fsqc/checkRotation.py
@@ -5,6 +5,7 @@
 
 # -----------------------------------------------------------------------------
 
+
 def checkRotation(subjects_dir, subject):
     """
     A function to determine the rotation angles of the Talairach transform.
@@ -24,44 +25,68 @@ def checkRotation(subjects_dir, subject):
         - unit of rotations is radians
 
     Requires valid mri/transforms/talairach.lta file and the 'transforms3d'
-    python packge. If not found, NaNs will be returned.
+    python package. If not found, NaNs will be returned.
 
     """
 
     #
 
-    import os, re
-    import numpy as np
+    import os
+    import re
+    import logging
+    import warnings
     import importlib.util
 
+    import numpy as np
+
+    # setttings
+
+    logging.captureWarnings(True)
+
+    # message
+
+    logging.info("Computing Talairach rotation angles ...")
+
     #
 
     if importlib.util.find_spec("transforms3d") is None:
-        print('\nWARNING: the \'transforms3d\' package is required for running this script, returning NaNs.\n')
+        warnings.warn("WARNING: the 'transforms3d' package is required for running this script, returning NaNs.")
         return np.nan, np.nan, np.nan
     else:
         import transforms3d as tr
 
-    # message
-
-    print("Computing Talairach rotation angles ...")
-
     # read talairach.lta
 
-    if not os.path.isfile(os.path.join(subjects_dir,subject,"mri","transforms","talairach.lta")):
-        print("WARNING: could not open "+os.path.join(subjects_dir,subject,"mri","transforms","talairach.lta")+", returning NaNs.")
+    if not os.path.isfile(
+        os.path.join(subjects_dir, subject, "mri", "transforms", "talairach.lta")
+    ):
+        warnings.warn(
+            "WARNING: could not open "
+            + os.path.join(subjects_dir, subject, "mri", "transforms", "talairach.lta")
+            + ", returning NaNs."
+        )
         return np.nan, np.nan, np.nan
 
-    with open(os.path.join(subjects_dir,subject,"mri","transforms","talairach.lta"), 'r') as datafile:
+    with open(
+        os.path.join(subjects_dir, subject, "mri", "transforms", "talairach.lta"), "r"
+    ) as datafile:
         lines = datafile.readlines()
 
     # get first four rows with three entries in exp notation
 
     mat = list()
     for line in lines:
-        res = re.search('^[\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+ [\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+ [\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+ [\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+', line)
+        res = re.search(
+            "^[\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+ [\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+ [\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+ [\\-0-9]+\\.[0-9]+e[\\-\\+][0-9]+",
+            line,
+        )
         if res is not None:
-            mat.append([ float(x) for x in res.group().replace('\n','').replace(';','').split() ])
+            mat.append(
+                [
+                    float(x)
+                    for x in res.group().replace("\n", "").replace(";", "").split()
+                ]
+            )
     mat = np.array(mat)
 
     # get translation, rotation, scale/zoom, and shear matrices
@@ -74,6 +99,14 @@ def checkRotation(subjects_dir, subject):
 
     rot_x, rot_y, rot_z = tr.euler.mat2euler(R)
 
-    print('Found Talairach rotation angles: x =','{:.3}'.format(rot_x),', y =','{:.3}'.format(rot_y),', z =','{:.3}'.format(rot_z),'radians.')
+    logging.info(
+        "Found Talairach rotation angles: x = " +
+        "{:.3}".format(rot_x) +
+        ", y = " +
+        "{:.3}".format(rot_y) +
+        ", z = " +
+        "{:.3}".format(rot_z) +
+        " radians.",
+    )
 
     return rot_x, rot_y, rot_z
diff --git a/qatoolspython/checkSNR.py b/fsqc/checkSNR.py
similarity index 66%
rename from qatoolspython/checkSNR.py
rename to fsqc/checkSNR.py
index 1660796..5a22adc 100644
--- a/qatoolspython/checkSNR.py
+++ b/fsqc/checkSNR.py
@@ -5,7 +5,14 @@
 
 # -----------------------------------------------------------------------------
 
-def checkSNR(subjects_dir, subject, nb_erode=3, ref_image="norm.mgz", aparc_image="aparc+aseg.mgz"):
+
+def checkSNR(
+    subjects_dir,
+    subject,
+    nb_erode=3,
+    ref_image="norm.mgz",
+    aparc_image="aparc+aseg.mgz",
+):
     """
     A function to check the SNR of the white and gray matter.
 
@@ -13,7 +20,7 @@ def checkSNR(subjects_dir, subject, nb_erode=3, ref_image="norm.mgz", aparc_imag
     matter segmentation is taken from the aparc+aseg image and the gray matter
     from the aseg image. The white matter is eroded by three voxels in order to
     ignore partial volumes. For the gray matter this is not possible, because
-    the layer is aready very thin. An erosion would eliminate nearly the whole
+    the layer is already very thin. An erosion would eliminate nearly the whole
     signal.
 
     Required arguments:
@@ -40,44 +47,50 @@ def checkSNR(subjects_dir, subject, nb_erode=3, ref_image="norm.mgz", aparc_imag
     # Imports
 
     import os
-    import numpy as np
+    import logging
+    import warnings
     import nibabel as nib
+    import numpy as np
     from skimage.morphology import binary_erosion
 
+    # Setttings
+
+    logging.captureWarnings(True)
+
     # Message
 
-    print("Computing white and gray matter SNR for "+ref_image+" ...")
+    logging.info("Computing white and gray matter SNR for " + ref_image + " ...")
 
     # Get data
 
-    try:
-        path_reference_image = os.path.join(subjects_dir,subject,"mri",ref_image)
+    path_reference_image = os.path.join(subjects_dir, subject, "mri", ref_image)
+    if os.path.exists(path_reference_image):
         norm = nib.load(path_reference_image)
         norm_data = norm.get_fdata()
-    except FileNotFoundError:
-        print("WARNING: could not open "+path_reference_image+", returning NaNs.")
+    else:
+        warnings.warn("WARNING: could not open " + path_reference_image + ", returning NaNs.")
         return np.nan, np.nan
 
-    try:
-        path_aseg = os.path.join(subjects_dir,subject,"mri","aseg.mgz")
+    path_aseg = os.path.join(subjects_dir, subject, "mri", "aseg.mgz")
+    if os.path.exists(path_aseg):
         aseg = nib.load(path_aseg)
         data_aseg = aseg.get_fdata()
-    except FileNotFoundError:
-        print("WARNING: could not open "+path_aseg+", returning NaNs.")
+    else:
+        warnings.warn("WARNING: could not open " + path_aseg + ", returning NaNs.")
         return np.nan, np.nan
 
-    try:
-        path_aparc_aseg  = os.path.join(subjects_dir,subject,"mri",aparc_image)
+    path_aparc_aseg = os.path.join(subjects_dir, subject, "mri", aparc_image)
+    if os.path.exists(path_aparc_aseg):
         inseg = nib.load(path_aparc_aseg)
         data_aparc_aseg = inseg.get_fdata()
-    except FileNotFoundError:
-        print("WARNING: could not open "+path_aparc_aseg+", returning NaNs.")
+    else:
+        warnings.warn("WARNING: could not open " + path_aparc_aseg + ", returning NaNs.")
         return np.nan, np.nan
 
     # Process white matter image
 
     # Create 3D binary data where the white matter locations are encoded with 1, all the others with zero
-    b_wm_data = np.zeros((256,256,256))
+    b_wm_data = np.zeros((256, 256, 256))
 
     # The following keys represent the white matter labels in the aparc+aseg image
     wm_labels = [2, 41, 7, 46, 251, 252, 253, 254, 255, 77, 78, 79]
@@ -85,27 +98,27 @@ def checkSNR(subjects_dir, subject, nb_erode=3, ref_image="norm.mgz", aparc_imag
     # Find the wm labels in the aparc+aseg image and set the locations in the binary image to one
     for i in wm_labels:
         x, y, z = np.where(data_aparc_aseg == i)
-        b_wm_data[x,y,z] = 1
+        b_wm_data[x, y, z] = 1
 
     # Erode white matter image
     nb_erode = nb_erode
-    b_wm_data = binary_erosion(b_wm_data,np.ones((nb_erode, nb_erode,nb_erode)))
+    b_wm_data = binary_erosion(b_wm_data, np.ones((nb_erode, nb_erode, nb_erode)))
 
     # Computation of the SNR of the white matter
     x, y, z = np.where(b_wm_data == 1)
-    signal_wm = norm_data[x,y,z]
+    signal_wm = norm_data[x, y, z]
     signal_wm_mean = np.mean(signal_wm)
     signal_wm_std = np.std(signal_wm)
-    wm_snr = signal_wm_mean/signal_wm_std
-    print("White matter signal to noise ratio:", '{:.4}'.format(wm_snr))
+    wm_snr = signal_wm_mean / signal_wm_std
+    logging.info("White matter signal to noise ratio: " + "{:.4}".format(wm_snr))
 
     # Process gray matter image
 
     # Create 3D binary data where the gray matter locations are encoded with 1, all the others with zero
-    b_gm_data = np.zeros((256,256,256))
+    b_gm_data = np.zeros((256, 256, 256))
 
     # The following keys represent the gray matter labels in the aseg image
-    gm_labels = [ 3, 42 ]
+    gm_labels = [3, 42]
 
     # Find the gm labels in the aseg image and set the locations in the binary image to one
     for i in gm_labels:
@@ -114,11 +127,11 @@ def checkSNR(subjects_dir, subject, nb_erode=3, ref_image="norm.mgz", aparc_imag
 
     # Computation of the SNR of the gray matter
     x, y, z = np.where(b_gm_data == 1)
-    signal_gm = norm_data[x,y,z]
+    signal_gm = norm_data[x, y, z]
     signal_gm_mean = np.mean(signal_gm)
     signal_gm_std = np.std(signal_gm)
-    gm_snr =signal_gm_mean/signal_gm_std
-    print ("Gray matter signal to noise ratio:", '{:.4}'.format(gm_snr))
+    gm_snr = signal_gm_mean / signal_gm_std
+    logging.info("Gray matter signal to noise ratio: " + "{:.4}".format(gm_snr))
 
     # Return
     return wm_snr, gm_snr
diff --git a/qatoolspython/checkTopology.py b/fsqc/checkTopology.py
similarity index 67%
rename from qatoolspython/checkTopology.py
rename to fsqc/checkTopology.py
index 3029a3a..b1ca9cf 100644
--- a/qatoolspython/checkTopology.py
+++ b/fsqc/checkTopology.py
@@ -5,6 +5,7 @@
 
 # -----------------------------------------------------------------------------
 
+
 def checkTopology(subjects_dir, subject):
     """
     A function to check the topology of left and right surfaces.
@@ -27,23 +28,30 @@ def checkTopology(subjects_dir, subject):
     # Imports
 
     import os
+    import logging
+    import warnings
+
     import numpy as np
 
+    # Setttings
+
+    logging.captureWarnings(True)
+
     # Message
 
-    print("Checking topology of the surfaces ...")
+    logging.info("Checking topology of the surfaces ...")
 
     # Get the logfile, and return with NaNs if unsuccessful:
 
-    path_log_file = os.path.join(subjects_dir,subject,"scripts","recon-all.log")
+    path_log_file = os.path.join(subjects_dir, subject, "scripts", "recon-all.log")
 
-    try:
-        with open(path_log_file, 'r') as logfile:
+    if os.path.exists(path_log_file):
+        with open(path_log_file, "r") as logfile:
             lines_log_file = logfile.read().splitlines()
-    except FileNotFoundError:
-            print("WARNING: could not find "+path_log_file+", returning NaNs.")
-            return np.nan, np.nan, np.nan, np.nan, np.nan, np.nan
-    
+    else:
+        warnings.warn("WARNING: could not find " + path_log_file + ", returning NaNs.")
+        return np.nan, np.nan, np.nan, np.nan, np.nan, np.nan
+
     # Initialize
 
     lh_holes = np.nan
@@ -59,35 +67,38 @@ def checkTopology(subjects_dir, subject):
     foundTopoLH = False
 
     for line_log_file in lines_log_file:
-
         # Look for the number of holes in the left and right hemisphere
         if "orig.nofix lhholes" in line_log_file:
             lh_holes = line_log_file.split()[3]
             lh_holes = lh_holes[:-1]
             lh_holes = int(lh_holes)
             rh_holes = line_log_file.split()[6]
-            print("Number of holes in the left hemisphere:", lh_holes)
-            print("Number of holes in the right hemisphere:", rh_holes)
+            logging.info("Number of holes in the left hemisphere: " + str(lh_holes))
+            logging.info("Number of holes in the right hemisphere: " + str(rh_holes))
 
         # Look for the number of defects
-        if "defects found" in line_log_file and foundDefectsLH is False :
+        if "defects found" in line_log_file and foundDefectsLH is False:
             lh_defects = line_log_file.split()[0]
             lh_defects = int(lh_defects)
-            print("Number of defects in the left hemisphere:", lh_defects)
+            logging.info("Number of defects in the left hemisphere: " + str(lh_defects))
             foundDefectsLH = True
-        elif "defects found" in line_log_file and foundDefectsLH is True :
+        elif "defects found" in line_log_file and foundDefectsLH is True:
             rh_defects = line_log_file.split()[0]
             rh_defects = int(rh_defects)
-            print("Number of defects in the right hemisphere:", rh_defects)
+            logging.info("Number of defects in the right hemisphere: " + str(rh_defects))
 
         # Look for the topological fixing time in the log file
         if "topology fixing took" in line_log_file and foundTopoLH is False:
             topo_time_lh = line_log_file.split()[3]
-            print("Topological fixing time for the left hemisphere:", topo_time_lh, "min")
+            logging.info(
+                "Topological fixing time for the left hemisphere: " + str(topo_time_lh) + " min"
+            )
             foundTopoLH = True
         elif "topology fixing took" in line_log_file and foundTopoLH is True:
             topo_time_rh = line_log_file.split()[3]
-            print("Topological fixing time for the right hemisphere:", topo_time_rh, "min")
+            logging.info(
+                "Topological fixing time for the right hemisphere: " + str(topo_time_rh) + " min"
+            )
 
     # Return
 
diff --git a/fsqc/cli/__init__.py b/fsqc/cli/__init__.py
new file mode 100644
index 0000000..ea7b765
--- /dev/null
+++ b/fsqc/cli/__init__.py
@@ -0,0 +1,21 @@
+"""
+fsqc CLI
+"""
+
+from ..fsqcMain import _parse_arguments, run_fsqc
+
+def main():
+
+    # say hello
+    print("")
+    print("-----------------------------")
+    print("fsqc")
+    print("-----------------------------")
+    print("")
+
+    # parse arguments
+    argsDict = _parse_arguments()
+
+    # run fsqc
+    if argsDict is not None:
+        run_fsqc(subjects_dir=None, output_dir=None, argsDict=argsDict)
diff --git a/fsqc/createScreenshots.py b/fsqc/createScreenshots.py
new file mode 100644
index 0000000..62f80b4
--- /dev/null
+++ b/fsqc/createScreenshots.py
@@ -0,0 +1,664 @@
+"""
+This module provides a function to create screenshots
+
+"""
+
+# -----------------------------------------------------------------------------
+
+
+def createScreenshots(
+    SUBJECT,
+    SUBJECTS_DIR,
+    OUTFILE,
+    INTERACTIVE=True,
+    LAYOUT=None,
+    BASE=["default"],
+    OVERLAY=["default"],
+    LABELS=None,
+    SURF=["default"],
+    SURFCOLOR=["default"],
+    VIEWS=["default"],
+    XLIM=None,
+    YLIM=None,
+    BINARIZE=False,
+    ORIENTATION=["radiological"],
+):
+    """
+    function createScreenshots()
+
+    BASE, VIEWS must be lists, can be ["default"]
+
+    OVERLAY, SURF, SURFCOLOR can be lists or None, can be ["default"]
+
+    XLIM, YLIM can be lists of list two-element numeric lists or None; if given,
+    length must match length of VIEWS. x and y refer to final image dimensions,
+    not MR volume dimensions.
+
+    """
+
+    # -----------------------------------------------------------------------------
+    # auxiliary functions
+
+    def computeLayout(n):
+        import numpy as np
+
+        y = np.ceil(np.sqrt(n))
+
+        x = y - np.divmod(y**2 - n, y)[0]
+
+        return int(x), int(y)
+
+    # -----------------------------------------------------------------------------
+    # imports
+
+    import os
+    import logging
+    import warnings
+    import matplotlib
+    import nibabel as nb
+    import numpy as np
+    import pandas as pd
+
+    if not INTERACTIVE:
+        matplotlib.use("Agg")
+
+    from matplotlib import pyplot as plt
+
+    from fsqc.fsqcUtils import levelsetsTria, returnFreeSurferColorLUT
+ 
+    # -----------------------------------------------------------------------------
+    # settings
+
+    logging.captureWarnings(True)
+
+    FIGSIZE = 8
+
+    FIGDPI = 100
+
+    ALPHA = 0.5
+
+    tol = 1e-16
+
+    # -----------------------------------------------------------------------------
+    # import image data
+
+    if BASE == ["default"]:
+        norm = nb.load(os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz"))
+    else:
+        norm = nb.load(BASE[0])
+
+    if OVERLAY is None:
+        aseg = None
+    elif OVERLAY == ["default"]:
+        aseg = nb.load(os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "aseg.mgz"))
+    else:
+        aseg = nb.load(OVERLAY[0])
+
+    # -----------------------------------------------------------------------------
+    # import surface data
+
+    if SURF == ["default"]:
+        surflist = [
+            os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "lh.white"),
+            os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "rh.white"),
+            os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "lh.pial"),
+            os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "rh.pial"),
+        ]
+    else:
+        surflist = SURF
+
+    surf = list()
+
+    if surflist is not None:
+        for i in range(len(surflist)):
+            surf.append(nb.freesurfer.io.read_geometry(surflist[i], read_metadata=True))
+
+    if SURFCOLOR == ["default"] and SURF == ["default"]:
+        surfcolor = ["yellow", "yellow", "red", "red"]
+    elif SURFCOLOR == ["default"] and SURF != ["default"]:
+        surfcolor = ["yellow"] * len(surf)
+    else:
+        surfcolor = SURFCOLOR
+
+    # -----------------------------------------------------------------------------
+    # import colortable, compute auxiliary variables, and transform to matplotlib
+    # colortable
+
+    lut = returnFreeSurferColorLUT()
+
+    # some fs7 labels are not present in fs6 LUT, check and add if necessary
+
+    if not (
+        np.isin(
+            list(range(231, 247)) + [801, 802, 803, 804, 805, 806, 807, 808, 809, 810],
+            lut[:, 0],
+        ).all()
+    ):
+        lutAdd = np.array(
+            (
+                [801, "L_hypothalamus_anterior_inferior", 250, 255, 50, 0],
+                [802, "L_hypothalamus_anterior_superior", 80, 200, 255, 0],
+                [803, "L_hypothalamus_posterior", 255, 160, 0, 0],
+                [804, "L_hypothalamus_tubular_inferior", 255, 160, 200, 0],
+                [805, "L_hypothalamus_tubular_superior", 20, 180, 130, 0],
+                [806, "R_hypothalamus_anterior_inferior", 250, 255, 50, 0],
+                [807, "R_hypothalamus_anterior_superior", 80, 200, 255, 0],
+                [808, "R_hypothalamus_posterior", 255, 160, 0, 0],
+                [809, "R_hypothalamus_tubular_inferior", 255, 160, 200, 0],
+                [810, "R_hypothalamus_tubular_superior", 20, 180, 130, 0],
+                [231, "HP_body", 0, 255, 0, 0],
+                [232, "HP_head", 255, 0, 0, 0],
+                [233, "presubiculum-head", 32, 0, 32, 0],
+                [234, "presubiculum-body", 64, 0, 64, 0],
+                [235, "subiculum-head", 0, 0, 175, 0],
+                [236, "subiculum-body", 0, 0, 255, 0],
+                [237, "CA1-head", 175, 75, 75, 0],
+                [238, "CA1-body", 255, 0, 0, 0],
+                [239, "CA3-head", 0, 80, 0, 0],
+                [240, "CA3-body", 0, 128, 0, 0],
+                [241, "CA4-head", 120, 90, 50, 0],
+                [242, "CA4-body", 196, 160, 128, 0],
+                [243, "GC-ML-DG-head", 75, 125, 175, 0],
+                [244, "GC-ML-DG-body", 32, 200, 255, 0],
+                [245, "molecular_layer_HP-head", 100, 25, 25, 0],
+                [246, "molecular_layer_HP-body", 128, 0, 0, 0],
+            ),
+            dtype=object,
+        )
+
+        lut = np.concatenate((lut, lutAdd), axis=0)
+
+    lutEnum = dict(zip(lut[:, 0], range(len(lut[:, 0]))))
+
+    lutTab = np.array(lut[:, (2, 3, 4, 5)] / 255, dtype="float32")
+    lutTab[:, 3] = 1
+
+    lutMap = matplotlib.colors.ListedColormap(lutTab)
+
+    # -----------------------------------------------------------------------------
+    # determine VIEWS
+
+    if VIEWS == ["default"]:
+        CutsRRAS = [("x", -10), ("x", 10), ("y", 0), ("z", 0)]
+    else:
+        CutsRRAS = VIEWS
+
+    # -----------------------------------------------------------------------------
+    # check if the chosen VIEWS are feasible. If not feasible changing to the nearest feasible values
+
+    m = norm.header.get_vox2ras_tkr()
+    n = norm.header.get_data_shape()
+
+    xyzIdx = np.array(
+        np.meshgrid(
+            np.linspace(0, n[0] - 1, n[0]),
+            np.linspace(0, n[1] - 1, n[1]),
+            np.linspace(0, n[2] - 1, n[2]),
+        )
+    )
+    xyzIdxFlat3 = np.reshape(xyzIdx, (3, np.prod(n))).transpose()
+    xyzIdxFlat4 = np.hstack((xyzIdxFlat3, np.ones((np.prod(n), 1))))
+    rasIdxFlat3 = np.matmul(m, xyzIdxFlat4.transpose()).transpose()[:, 0:3]
+
+    for i, icr in enumerate(CutsRRAS):
+        if icr[0] == "x":
+            iDim = 0
+        elif icr[0] == "y":
+            iDim = 1
+        elif icr[0] == "z":
+            iDim = 2
+
+        if not np.any(rasIdxFlat3[:, iDim] == icr[1]):
+            closestCutValue = rasIdxFlat3[
+                np.abs(rasIdxFlat3[:, iDim] - icr[1]).argmin(), iDim
+            ]
+            logging.info(
+                f"INFO: the VIEW {icr} will be changed to ('{icr[0]}', {closestCutValue:.2f}) so it is not"
+                " necessary to interpolate volumetric data"
+            )
+            CutsRRAS[i] = (icr[0], closestCutValue)
+
+    # -----------------------------------------------------------------------------
+    # compute levelsets
+
+    LVL = list()
+
+    # will not run if surf is empty (intended)
+    for s in range(len(surf)):
+        sLVL = list()
+
+        for i in range(len(CutsRRAS)):
+            # determine dimension
+            if CutsRRAS[i][0] == "x":
+                iDim = 0
+            elif CutsRRAS[i][0] == "y":
+                iDim = 1
+            elif CutsRRAS[i][0] == "z":
+                iDim = 2
+
+            # compute levelsets: e.g., LVL[SURF][VIEWS][vLVL|tLVL|iLVL][0][elementDim1][elementDim2]
+            sLVL.append(
+                levelsetsTria(
+                    surf[s][0], surf[s][1], surf[s][0][:, iDim], CutsRRAS[i][1]
+                )
+            )
+
+        LVL.append(sLVL)
+
+    # -----------------------------------------------------------------------------
+    # get data for norm
+
+    normData = norm.get_fdata()
+
+    normVals = normData
+
+    # -----------------------------------------------------------------------------
+    # get data for aseg and change to enumerated aseg so that it can be used as
+    # index to lutMap
+
+    if aseg is not None:
+        asegData = aseg.get_fdata()
+
+        if LABELS is not None:
+            asegData = asegData * np.isin(asegData, LABELS)
+
+        if BINARIZE is True:
+            asegData = (asegData > 0).astype(int)
+
+        asegUnique, asegIdx = np.unique(asegData, return_inverse=True)
+
+        asegEnum = np.array([lutEnum[x] for x in asegUnique])
+
+        asegVals = np.reshape(asegEnum[asegIdx], (aseg.shape))
+
+    # -----------------------------------------------------------------------------
+    # compile image data for plotting
+
+    # note that we are using the TkReg RAS system, not RAS per se
+    # this is because the surfaces are in TkReg RAS also
+
+    # one considerable advantage of the (TkReg) RAS system is that it is solely
+    # based on switching dimensions, not rotating at odd angles etc, so no need
+    # for interpolating.
+
+    # x_R y_R z_R c_R
+    # x_A y_A z_A c_A
+    # x_S y_S z_S c_S
+    #   0   0   0   1
+
+    # m = norm.header.get_vox2ras_tkr()
+    # n = norm.header.get_data_shape()
+
+    # xyzIdx = np.array(np.meshgrid(np.linspace(0, n[0] - 1, n[0]), np.linspace(0, n[1] - 1, n[1]), np.linspace(0, n[2] - 1, n[2])))
+    # xyzIdxFlat3 = np.reshape(xyzIdx, (3, np.prod(n))).transpose()
+    # xyzIdxFlat4 = np.hstack((xyzIdxFlat3, np.ones((np.prod(n), 1))))
+    # rasIdxFlat3 = np.matmul(m, xyzIdxFlat4.transpose()).transpose()[:, 0:3]
+
+    normValsRAS = list()
+    if aseg is not None:
+        asegValsRAS = list()
+
+    for i in range(len(CutsRRAS)):
+        # determine dimension
+        if CutsRRAS[i][0] == "x":
+            iDim = 0
+        elif CutsRRAS[i][0] == "y":
+            iDim = 1
+        elif CutsRRAS[i][0] == "z":
+            iDim = 2
+
+        sel = np.array(
+            xyzIdxFlat3[rasIdxFlat3[:, iDim] == CutsRRAS[i][1], :], dtype="int"
+        )
+        normValsRAS.append(
+            np.squeeze(
+                normVals[
+                    np.min(sel[:, 0]):np.max(sel[:, 0]) + 1,
+                    np.min(sel[:, 1]):np.max(sel[:, 1]) + 1,
+                    np.min(sel[:, 2]):np.max(sel[:, 2]) + 1,
+                ]
+            )
+        )
+        if aseg is not None:
+            asegValsRAS.append(
+                np.squeeze(
+                    asegVals[
+                        np.min(sel[:, 0]):np.max(sel[:, 0]) + 1,
+                        np.min(sel[:, 1]):np.max(sel[:, 1]) + 1,
+                        np.min(sel[:, 2]):np.max(sel[:, 2]) + 1,
+                    ]
+                )
+            )
+
+    # -----------------------------------------------------------------------------
+    # plotting: create a new figure, plot into it, then close it so it never gets
+    # displayed
+
+    # turn interactive plotting off unless interactive
+    if not INTERACTIVE:
+        plt.ioff()
+
+    # compute layout
+    if LAYOUT is None:
+        myLayout = computeLayout(len(CutsRRAS))
+    else:
+        myLayout = LAYOUT
+
+    # create list of de-facto layouts
+    myLayoutList = list()
+    for axsx in range(myLayout[0]):
+        for axsy in range(myLayout[1]):
+            myLayoutList.append((axsx, axsy))
+
+    # create subplots
+    fig, axs = plt.subplots(myLayout[0], myLayout[1])
+    axs = np.reshape(axs, myLayout)
+
+    # adjust layout
+    fig.set_size_inches([FIGSIZE * myLayout[1], FIGSIZE * myLayout[0]])
+    fig.set_dpi(FIGDPI)
+    fig.set_facecolor("black")
+    fig.set_tight_layout({"pad": 0})
+    fig.subplots_adjust(wspace=0)
+
+    # -----------------------------------------------------------------------------
+    # plot each panel
+
+    for p in range(len(CutsRRAS)):
+        logging.info("Panel " + str(p))
+
+        axsx = myLayoutList[p][0]
+        axsy = myLayoutList[p][1]
+
+        # determine dimensions
+        if CutsRRAS[p][0] == "x":
+            # x axis of the image should be towards anterior, y axis should be towards superior in RAS image
+            dims = (1, 2)
+            # determine extent
+            extent = (
+                rasIdxFlat3[0, dims[0]],
+                rasIdxFlat3[-1, dims[0]],
+                rasIdxFlat3[0, dims[1]],
+                rasIdxFlat3[-1, dims[1]],
+            )
+            # imshow puts the first dimension (rows) of the data on the y axis, and the second (columns) on the x axis
+            cor = np.where(m[dims[0], 0:3])[0]
+            axi = np.where(m[dims[1], 0:3])[0]
+            #
+            if axi < cor:
+                axs[axsx, axsy].imshow(
+                    normValsRAS[p], cmap="gray", origin="lower", extent=extent
+                )
+                if aseg is not None:
+                    axs[axsx, axsy].imshow(
+                        asegValsRAS[p] + 0.5,
+                        cmap=lutMap,
+                        origin="lower",
+                        extent=extent,
+                        vmin=0,
+                        vmax=len(lutTab),
+                        alpha=ALPHA,
+                    )
+            else:
+                axs[axsx, axsy].imshow(
+                    normValsRAS[p].transpose(),
+                    cmap="gray",
+                    origin="lower",
+                    extent=extent,
+                )
+                if aseg is not None:
+                    axs[axsx, axsy].imshow(
+                        asegValsRAS[p].transpose() + 0.5,
+                        cmap=lutMap,
+                        origin="lower",
+                        extent=extent,
+                        vmin=0,
+                        vmax=len(lutTab),
+                        alpha=ALPHA,
+                    )
+        elif CutsRRAS[p][0] == "y":
+            # x axis of the image should be towards right, y axis should be towards superior in RAS image
+            dims = (0, 2)
+            # determine extent
+            extent = (
+                rasIdxFlat3[0, dims[0]],
+                rasIdxFlat3[-1, dims[0]],
+                rasIdxFlat3[0, dims[1]],
+                rasIdxFlat3[-1, dims[1]],
+            )
+            # imshow puts the first dimension (rows) of the data on the y axis, and the second (columns) on the x axis
+            sag = np.where(m[dims[0], 0:3])[0]
+            axi = np.where(m[dims[1], 0:3])[0]
+            #
+            if axi < sag:
+                axs[axsx, axsy].imshow(
+                    normValsRAS[p], cmap="gray", origin="lower", extent=extent
+                )
+                if aseg is not None:
+                    axs[axsx, axsy].imshow(
+                        asegValsRAS[p] + 0.5,
+                        cmap=lutMap,
+                        origin="lower",
+                        extent=extent,
+                        vmin=0,
+                        vmax=len(lutTab),
+                        alpha=ALPHA,
+                    )
+            else:
+                axs[axsx, axsy].imshow(
+                    normValsRAS[p].transpose(),
+                    cmap="gray",
+                    origin="lower",
+                    extent=extent,
+                )
+                if aseg is not None:
+                    axs[axsx, axsy].imshow(
+                        asegValsRAS[p].transpose() + 0.5,
+                        cmap=lutMap,
+                        origin="lower",
+                        extent=extent,
+                        vmin=0,
+                        vmax=len(lutTab),
+                        alpha=ALPHA,
+                    )
+        elif CutsRRAS[p][0] == "z":
+            # x axis of the image should be towards right, y axis should be towards anterior in RAS image
+            dims = (0, 1)
+            # determine extent
+            extent = (
+                rasIdxFlat3[0, dims[0]],
+                rasIdxFlat3[-1, dims[0]],
+                rasIdxFlat3[0, dims[1]],
+                rasIdxFlat3[-1, dims[1]],
+            )
+            # imshow puts the first dimension (rows) of the data on the y axis, and the second (columns) on the x axis
+            sag = np.where(m[dims[0], 0:3])[0]
+            cor = np.where(m[dims[1], 0:3])[0]
+            if cor < sag:
+                axs[axsx, axsy].imshow(
+                    normValsRAS[p], cmap="gray", origin="lower", extent=extent
+                )
+                if aseg is not None:
+                    axs[axsx, axsy].imshow(
+                        asegValsRAS[p] + 0.5,
+                        cmap=lutMap,
+                        origin="lower",
+                        extent=extent,
+                        vmin=0,
+                        vmax=len(lutTab),
+                        alpha=ALPHA,
+                    )
+            else:
+                axs[axsx, axsy].imshow(
+                    normValsRAS[p].transpose(),
+                    cmap="gray",
+                    origin="lower",
+                    extent=extent,
+                )
+                if aseg is not None:
+                    axs[axsx, axsy].imshow(
+                        asegValsRAS[p].transpose() + 0.5,
+                        cmap=lutMap,
+                        origin="lower",
+                        extent=extent,
+                        vmin=0,
+                        vmax=len(lutTab),
+                        alpha=ALPHA,
+                    )
+
+        # prepare plot
+        if rasIdxFlat3[0, dims[0]] > rasIdxFlat3[-1, dims[0]]:
+            axs[axsx, axsy].invert_xaxis()
+        if rasIdxFlat3[0, dims[1]] > rasIdxFlat3[-1, dims[1]]:
+            axs[axsx, axsy].invert_yaxis()
+
+        axs[axsx, axsy].set_axis_off()
+        axs[axsx, axsy].set_aspect("equal")
+
+        if XLIM is not None:
+            axs[axsx, axsy].set_xlim(XLIM[p])
+
+        if YLIM is not None:
+            axs[axsx, axsy].set_ylim(YLIM[p])
+
+        # determine left-right orientation for coronal and axial views
+        if ORIENTATION == ["radiological"]:
+            if CutsRRAS[p][0] == "y" or CutsRRAS[p][0] == "z":
+                axs[axsx, axsy].invert_xaxis()
+
+        # now plot
+        for s in range(len(surf)):
+            if len(LVL[s][p][0][0]) > 0:
+                logging.info("Surface " + str(s))
+
+                # create array of line segments
+                tmpx = list()
+                tmpy = list()
+
+                for i in range(len(LVL[s][p][1][0])):
+                    tmpx.append(
+                        (
+                            LVL[s][p][0][0][LVL[s][p][1][0][i][0] - 1][dims[0]],
+                            LVL[s][p][0][0][LVL[s][p][1][0][i][1] - 1][dims[0]],
+                        )
+                    )
+                    tmpy.append(
+                        (
+                            LVL[s][p][0][0][LVL[s][p][1][0][i][0] - 1][dims[1]],
+                            LVL[s][p][0][0][LVL[s][p][1][0][i][1] - 1][dims[1]],
+                        )
+                    )
+
+                tmpx = np.array(tmpx)
+                tmpy = np.array(tmpy)
+
+                # remove duplicate points
+                tmpxy = np.unique(np.concatenate((tmpx, tmpy), axis=1), axis=0)
+                tmpx = tmpxy[:, 0:2]
+                tmpy = tmpxy[:, 2:4]
+
+                # remove segments which are de-facto points
+                tmpIdx = np.logical_or(
+                    np.abs(tmpx[:, 0] - tmpx[:, 1]) > tol,
+                    np.abs(tmpy[:, 0] - tmpy[:, 1]) > tol,
+                )
+                tmpx = tmpx[tmpIdx, :]
+                tmpy = tmpy[tmpIdx, :]
+
+                # need to order array of line segments; whenever we encounter a
+                # closed loop, we will already plot; otherwise, plot in the end
+                sortIdx = np.array(range(0, len(tmpx)))
+
+                tmpxSort = np.array(tmpx[sortIdx[0],], ndmin=2)
+                tmpySort = np.array(tmpy[sortIdx[0],], ndmin=2)
+
+                sortIdx = np.delete(sortIdx, sortIdx[0])
+
+                while len(sortIdx) > 1:
+                    findIdx = np.array(
+                        np.where(
+                            np.logical_and(
+                                np.abs(
+                                    tmpx[sortIdx,] - tmpxSort[tmpxSort.shape[0] - 1, 1]
+                                )
+                                < tol,
+                                np.abs(
+                                    tmpy[sortIdx,] - tmpySort[tmpySort.shape[0] - 1, 1]
+                                )
+                                < tol,
+                            )
+                        ),
+                        ndmin=2,
+                    ).T
+
+                    # delete existing finds
+                    findIdxKeep = list()
+                    for k in range(findIdx.shape[0]):
+                        if not np.any(
+                            np.all(
+                                np.logical_or(
+                                    tmpx[sortIdx[findIdx[k, 0]], 0] == tmpxSort,
+                                    tmpx[sortIdx[findIdx[k, 0]], 1] == tmpxSort,
+                                ),
+                                axis=1,
+                            )
+                        ):
+                            findIdxKeep.append(k)
+                    findIdx = findIdx[findIdxKeep,]
+
+                    if findIdx.shape[0] == 0:
+                        # close loop and plot already
+                        axs[axsx, axsy].plot(
+                            tmpxSort,
+                            tmpySort,
+                            color=surfcolor[s],
+                            linewidth=np.round(FIGSIZE / 8),
+                        )
+                        # reset (start new loop)
+                        tmpxSort = np.array(tmpx[sortIdx[0],], ndmin=2)
+                        tmpySort = np.array(tmpy[sortIdx[0],], ndmin=2)
+                        sortIdx = np.delete(sortIdx, 0)
+                    elif findIdx.shape[0] == 1:
+                        # add to current set
+                        if findIdx[0, 1] == 0:
+                            tmpxSort = np.append(
+                                tmpxSort,
+                                np.array(tmpx[sortIdx[findIdx[0, 0]], ::1], ndmin=2),
+                                axis=0,
+                            )
+                            tmpySort = np.append(
+                                tmpySort,
+                                np.array(tmpy[sortIdx[findIdx[0, 0]], ::1], ndmin=2),
+                                axis=0,
+                            )
+                        elif findIdx[0, 1] == 1:
+                            tmpxSort = np.append(
+                                tmpxSort,
+                                np.array(tmpx[sortIdx[findIdx[0, 0]], ::-1], ndmin=2),
+                                axis=0,
+                            )
+                            tmpySort = np.append(
+                                tmpySort,
+                                np.array(tmpy[sortIdx[findIdx[0, 0]], ::-1], ndmin=2),
+                                axis=0,
+                            )
+                        sortIdx = np.delete(sortIdx, findIdx[0, 0])
+                    elif findIdx.shape[0] > 1:
+                        warnings.warn("WARNING: a problem occurred with the surface overlays")
+                # now final plot
+                axs[axsx, axsy].plot(
+                    tmpxSort,
+                    tmpySort,
+                    color=surfcolor[s],
+                    linewidth=np.round(FIGSIZE / 8),
+                )
+
+    # -----------------------------------------------------------------------------
+    # output
+
+    if not INTERACTIVE:
+        plt.savefig(OUTFILE, facecolor=fig.get_facecolor())
+        plt.close(fig)
+
+    # -----------------------------------------------------------------------------
+    #
diff --git a/fsqc/createSurfacePlots.py b/fsqc/createSurfacePlots.py
new file mode 100644
index 0000000..e8ab981
--- /dev/null
+++ b/fsqc/createSurfacePlots.py
@@ -0,0 +1,154 @@
+"""
+This module provides a function to create surface plots
+
+"""
+
+# -----------------------------------------------------------------------------
+
+
+def createSurfacePlots(SUBJECT, SUBJECTS_DIR, SURFACES_OUTDIR, VIEWS, FASTSURFER):
+    """
+    function createSurfacePlots
+
+    """
+
+    # -----------------------------------------------------------------------------
+    # imports
+
+    import os
+
+    import lapy as lp
+    import nibabel as nb
+    import numpy as np
+    from lapy import plot as lpp
+
+    # -----------------------------------------------------------------------------
+    # settings
+    _views_available = [
+        ("anterior", 0, 2, 0),
+        ("posterior", 0, -2, 0),
+        ("left", -2, 0, 0),
+        ("right", 2, 0, 0),
+        ("superior", 0, 0, 2),
+        ("inferior", 0, 0, -2),
+    ]
+    scale_png = 0.8
+    
+    # -----------------------------------------------------------------------------
+    # import surfaces and overlays
+
+    triaPialL = nb.freesurfer.read_geometry(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "lh.pial")
+    )
+    triaPialR = nb.freesurfer.read_geometry(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "rh.pial")
+    )
+    triaInflL = nb.freesurfer.read_geometry(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "lh.inflated")
+    )
+    triaInflR = nb.freesurfer.read_geometry(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "surf", "rh.inflated")
+    )
+
+    triaPialL = lp.TriaMesh(triaPialL[0], triaPialL[1])
+    triaPialR = lp.TriaMesh(triaPialR[0], triaPialR[1])
+    triaInflL = lp.TriaMesh(triaInflL[0], triaInflL[1])
+    triaInflR = lp.TriaMesh(triaInflR[0], triaInflR[1])
+
+    if FASTSURFER is True:
+        annotL = nb.freesurfer.read_annot(
+            os.path.join(SUBJECTS_DIR, SUBJECT, "label", "lh.aparc.DKTatlas.annot"), orig_ids=False
+        )
+        annotR = nb.freesurfer.read_annot(
+            os.path.join(SUBJECTS_DIR, SUBJECT, "label", "rh.aparc.DKTatlas.annot"), orig_ids=False
+        )
+    else:
+        annotL = nb.freesurfer.read_annot(
+            os.path.join(SUBJECTS_DIR, SUBJECT, "label", "lh.aparc.annot"), orig_ids=False
+        )
+        annotR = nb.freesurfer.read_annot(
+            os.path.join(SUBJECTS_DIR, SUBJECT, "label", "rh.aparc.annot"), orig_ids=False
+        )
+
+    # -----------------------------------------------------------------------------
+    # plots
+
+    # check if annotation has labels that are not included in the colortable
+    if any(annotL[0] == -1):
+        # prepend colortable and update indices
+        ctabL = np.concatenate((np.mat([127, 127, 127]), annotL[1][:, 0:3]), axis=0)
+        indsL = annotL[0] + 1
+    else:
+        ctabL = annotL[1][:, 0:3]
+        indsL = annotL[0]
+
+    vAnnotL = ctabL[indsL, :]
+
+    # check if annotation has labels that are not included in the colortable
+    if any(annotR[0] == -1):
+        # prepend colortable and update indices
+        ctabR = np.concatenate((np.mat([127, 127, 127]), annotR[1][:, 0:3]), axis=0)
+        indsR = annotR[0] + 1
+    else:
+        ctabR = annotR[1][:, 0:3]
+        indsR = annotR[0]
+
+    vAnnotR = ctabR[indsR, :]
+
+    # -----------------------------------------------------------------------------
+    # plots
+
+    for view, x, y, z in _views_available:
+        fpath_lp = os.path.join(SURFACES_OUTDIR, f"lh.pial.{view}.png")
+        fpath_rp = os.path.join(SURFACES_OUTDIR, f"rh.pial.{view}.png")
+        fpath_li = os.path.join(SURFACES_OUTDIR, f"lh.inflated.{view}.png")
+        fpath_ri = os.path.join(SURFACES_OUTDIR, f"rh.inflated.{view}.png")
+
+        if view in VIEWS:
+            camera = dict(
+                up=dict(x=0, y=0, z=1),
+                center=dict(x=0, y=0, z=0),
+                eye=dict(x=x, y=y, z=z),
+            )
+
+            lpp.plot_tria_mesh(
+                triaPialL,
+                vcolor=vAnnotL,
+                background_color="black",
+                camera=camera,
+                export_png=fpath_lp,
+                no_display=True,
+                scale_png=scale_png,
+            )
+            lpp.plot_tria_mesh(
+                triaPialR,
+                vcolor=vAnnotR,
+                background_color="black",
+                camera=camera,
+                export_png=fpath_rp,
+                no_display=True,
+                scale_png=scale_png,
+            )
+            lpp.plot_tria_mesh(
+                triaInflL,
+                vcolor=vAnnotL,
+                background_color="black",
+                camera=camera,
+                export_png=fpath_li,
+                no_display=True,
+                scale_png=scale_png,
+            )
+            lpp.plot_tria_mesh(
+                triaInflR,
+                vcolor=vAnnotR,
+                background_color="black",
+                camera=camera,
+                export_png=fpath_ri,
+                no_display=True,
+                scale_png=scale_png,
+            )
+        else:
+            # remove images potentially created in earlier run but not updated now
+            for fpath in [fpath_lp, fpath_rp, fpath_li, fpath_ri]:
+                if os.path.isfile(fpath):
+                    os.remove(fpath)
diff --git a/fsqc/evaluateFornixSegmentation.py b/fsqc/evaluateFornixSegmentation.py
new file mode 100644
index 0000000..c3e2b92
--- /dev/null
+++ b/fsqc/evaluateFornixSegmentation.py
@@ -0,0 +1,192 @@
+"""
+This module provides a function to evaluate potential missegmentation of the fornix
+
+"""
+
+# -----------------------------------------------------------------------------
+
+
+def evaluateFornixSegmentation(
+    SUBJECT,
+    SUBJECTS_DIR,
+    OUTPUT_DIR,
+    CREATE_SCREENSHOT=True,
+    SCREENSHOTS_OUTFILE=[],
+    RUN_SHAPEDNA=True,
+    N_EIGEN=15,
+):
+    """
+    A function to evaluate potential missegmentation of the fornix.
+
+    This script evaluates potential missegmentation of the fornix, which may
+    erroneously be attached to the 'corpus collosum' label.
+
+    It will appply the cc_up.lta transform to the norm.mgz and the aseg files,
+    and create a binary corpus callosum mask and surface. Resulting files are
+    saved to subject-specific directories within the 'fornix' subdirectory of
+    the output directory.
+
+    If the corresponding arguments are set to 'True', the script will also
+    create screenshots and run a shape analysis of the corpus callosum surface.
+    Resulting files will be saved to the same directory as indicated above.
+
+    Required arguments:
+        - SUBJECT
+        - SUBJECTS_DIR
+        - OUTPUT_DIR
+
+    Optional arguments:
+        - CREATE_SCREENSHOT <bool> (default: True)
+        - SCREENSHOTS_OUTFILE <string> or empty list (default: [])
+        - RUN_SHAPEDNA <bool> (default: True)
+        - N_EIGEN <int> number of Eigenvalues for shape analysis (default: 30)
+
+    Requires (if not found, returns NaNs):
+        - mri/transforms/cc_up.lta
+        - mri/aseg.mgz
+        - mri/norm.mgz
+
+    Returns:
+        - a numpy array of N_EIGEN eigenvalues if RUN_SHAPEDNA is True,
+          otherwise a numpy array of NaNs of the same dimension
+
+    """
+
+    # --------------------------------------------------------------------------
+    # imports
+
+    import os
+    import logging
+    import warnings
+    import numpy as np
+
+    from fsqc.createScreenshots import createScreenshots
+    from fsqc.fsqcUtils import applyTransform, binarizeImage
+
+    # --------------------------------------------------------------------------
+    # check files
+
+    logging.captureWarnings(True)
+
+    if not os.path.isfile(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "transforms", "cc_up.lta")
+    ):
+        warnings.warn(
+            "WARNING: could not find "
+            + os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "transforms", "cc_up.lta")
+            + ", returning NaNs"
+        )
+
+        out = np.empty(N_EIGEN)
+        out[:] = np.nan
+
+        return out
+
+    elif not os.path.isfile(os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "aseg.mgz")):
+        warnings.warn(
+            "WARNING: could not find "
+            + os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "aseg.mgz")
+            + ", returning NaNs"
+        )
+
+        out = np.empty(N_EIGEN)
+        out[:] = np.nan
+
+        return out
+
+    elif not os.path.isfile(os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")):
+        warnings.warn(
+            "WARNING: could not find "
+            + os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")
+            + ", returning NaNs"
+        )
+
+        out = np.empty(N_EIGEN)
+        out[:] = np.nan
+
+        return out
+
+    if not SCREENSHOTS_OUTFILE:
+        SCREENSHOTS_OUTFILE = os.path.join(OUTPUT_DIR, "cc.png")
+
+    # --------------------------------------------------------------------------
+    # conduct transform for aseg and norm
+
+    applyTransform(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "aseg.mgz"),
+        os.path.join(OUTPUT_DIR, "asegCCup.mgz"),
+        mat_file=os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "transforms", "cc_up.lta"),
+        interp="nearest",
+    )
+
+    # when using 'make_upright', conducting the transform for norm.mgz is no
+    # longer necessary (and will produce the same results)
+
+    applyTransform(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz"),
+        os.path.join(OUTPUT_DIR, "normCCup.mgz"),
+        mat_file=os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "transforms", "cc_up.lta"),
+        interp="cubic",
+    )
+
+    # create fornix mask
+
+    binarizeImage(
+        os.path.join(OUTPUT_DIR, "asegCCup.mgz"),
+        os.path.join(OUTPUT_DIR, "cc.mgz"),
+        match=[251, 252, 253, 254, 255],
+    )
+
+    # --------------------------------------------------------------------------
+    # create screenshot
+
+    if CREATE_SCREENSHOT is True:
+        # Note: SURF = [os.path.join(OUTPUT_DIR,"cc.surf")] is no longer possible
+        # unless using FreeSurfer's mri_binarize function (or creating the fornix
+        # surface otherwise)
+        createScreenshots(
+            SUBJECT=SUBJECT,
+            SUBJECTS_DIR=SUBJECTS_DIR,
+            INTERACTIVE=False,
+            VIEWS=[("x", -2), ("x", 0), ("x", 2)],
+            LAYOUT=(1, 3),
+            BASE=[os.path.join(OUTPUT_DIR, "normCCup.mgz")],
+            OVERLAY=[os.path.join(OUTPUT_DIR, "cc.mgz")],
+            SURF=None,
+            OUTFILE=SCREENSHOTS_OUTFILE,
+        )
+
+    # --------------------------------------------------------------------------
+    # run shapeDNA
+
+    if RUN_SHAPEDNA is True:
+        import nibabel as nb
+        from lapy import TriaMesh, shapedna
+
+        surf = nb.freesurfer.io.read_geometry(
+            os.path.join(OUTPUT_DIR, "cc.surf"), read_metadata=True
+        )
+
+        evDict = shapedna.compute_shapedna(TriaMesh(v=surf[0], t=surf[1]), k=N_EIGEN)
+
+        ev = evDict['Eigenvalues']
+        evec = evDict['Eigenvectors']
+
+        d = dict()
+        d["Refine"] = 0
+        d["Degree"] = 1
+        d["Dimension"] = 2
+        d["Elements"] = len(surf[1])
+        d["DoF"] = len(surf[0])
+        d["NumEW"] = N_EIGEN
+        d["Eigenvalues"] = ev
+        d["Eigenvectors"] = evec
+
+        # return
+        return d["Eigenvalues"]
+
+    else:
+        out = np.empty(N_EIGEN)
+        out[:] = np.nan
+
+        return out
diff --git a/fsqc/evaluateHippocampalSegmentation.py b/fsqc/evaluateHippocampalSegmentation.py
new file mode 100644
index 0000000..3c18096
--- /dev/null
+++ b/fsqc/evaluateHippocampalSegmentation.py
@@ -0,0 +1,182 @@
+"""
+This module provides a function to evaluate potential missegmentation of the
+hippocampus and amygdala
+
+"""
+
+# -----------------------------------------------------------------------------
+
+
+def evaluateHippocampalSegmentation(
+    SUBJECT,
+    SUBJECTS_DIR,
+    OUTPUT_DIR,
+    CREATE_SCREENSHOT=True,
+    SCREENSHOTS_OUTFILE=[],
+    SCREENSHOTS_ORIENTATION=["radiological"],
+    HEMI="lh",
+    LABEL="T1.v21",
+):
+    """
+    A function to evaluate potential missegmentation of the hippocampus and
+    amygdala.
+
+    This script evaluates potential missegmentation of the hippocampus and
+    amygdala as created by FreeSurfer 7.1's dedicated module:
+    https://surfer.nmr.mgh.harvard.edu/fswiki/HippocampalSubfieldsAndNucleiOfAmygdala
+
+    If the corresponding arguments are set to 'True', the script will also
+    create screenshots. Resulting files will be saved to the same directory
+    as indicated above.
+
+    Required arguments:
+        - SUBJECT
+        - SUBJECTS_DIR
+        - OUTPUT_DIR
+
+    Optional arguments:
+        - CREATE_SCREENSHOT <bool> (default: True)
+        - SCREENSHOTS_OUTFILE <string> or empty list (default: [])
+        - HEMI (default: lh)
+        - LABEL (default: T1.v21)
+
+    Required files:
+        - mri/[lr]h.hippoAmygLabels-<LABEL>.FSvoxelSpace.mgz
+        - mri/norm.mgz
+
+    """
+
+    # --------------------------------------------------------------------------
+    # imports
+
+    import os
+    import logging
+
+    import nibabel as nb
+    import numpy as np
+    from scipy import ndimage
+
+    from fsqc.createScreenshots import createScreenshots
+    from fsqc.fsqcUtils import binarizeImage
+
+    # --------------------------------------------------------------------------
+    # check files
+
+    if not os.path.isfile(os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")):
+        logging.error(
+            "ERROR: could not find "
+            + os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")
+            + ", not running hippocampus module."
+        )
+
+        raise ValueError("File not found")
+
+    if not os.path.isfile(
+        os.path.join(
+            SUBJECTS_DIR,
+            SUBJECT,
+            "mri",
+            HEMI + ".hippoAmygLabels-" + LABEL + ".FSvoxelSpace.mgz",
+        )
+    ):
+        logging.error(
+            "ERROR: could not find "
+            + os.path.join(
+                SUBJECTS_DIR,
+                SUBJECT,
+                "mri",
+                HEMI + ".hippoAmygLabels-" + LABEL + ".FSvoxelSpace.mgz",
+            )
+            + ", not running hippocampus module."
+        )
+
+        raise ValueError("File not found")
+
+    if not SCREENSHOTS_OUTFILE:
+        SCREENSHOTS_OUTFILE = os.path.join(OUTPUT_DIR, "hippocampus.png")
+
+    # --------------------------------------------------------------------------
+    # create mask
+
+    binarizeImage(
+        os.path.join(
+            SUBJECTS_DIR,
+            SUBJECT,
+            "mri",
+            HEMI + ".hippoAmygLabels-" + LABEL + ".FSvoxelSpace.mgz",
+        ),
+        os.path.join(OUTPUT_DIR, "hippocampus-" + HEMI + ".mgz"),
+        match=None,
+    )
+
+    # --------------------------------------------------------------------------
+    # get centroids
+
+    seg = nb.load(
+        os.path.join(
+            SUBJECTS_DIR,
+            SUBJECT,
+            "mri",
+            HEMI + ".hippoAmygLabels-" + LABEL + ".FSvoxelSpace.mgz",
+        )
+    )
+    seg_data = seg.get_fdata()
+    seg_labels = np.setdiff1d(np.unique(seg_data), 0)
+
+    centroids = np.array(ndimage.center_of_mass(seg_data, seg_data, seg_labels))
+    centroids = np.concatenate((seg_labels[:, np.newaxis], centroids), axis=1)
+
+    vox2ras_tkr = seg.header.get_vox2ras_tkr()
+
+    ctr_tkr = np.concatenate(
+        (centroids[:, 1:4], np.ones((centroids.shape[0], 1))), axis=1
+    )
+    ctr_tkr = np.matmul(vox2ras_tkr, ctr_tkr.T).T
+    ctr_tkr = np.concatenate(
+        (np.array(centroids[:, 0], ndmin=2).T, ctr_tkr[:, 0:3]), axis=1
+    )
+
+    # [7004, 237, 238]
+
+    ctr_tkr_x0 = ctr_tkr[np.argwhere(ctr_tkr[:, 0] == 237), 1]
+    ctr_tkr_y0 = ctr_tkr[np.argwhere(ctr_tkr[:, 0] == 237), 2]
+    ctr_tkr_z0 = ctr_tkr[np.argwhere(ctr_tkr[:, 0] == 237), 3]
+
+    # set ranges for cropping the image (assuming RAS coordinates)
+
+    ctr_tkr_x0_xlim = [ctr_tkr_y0 - 40, ctr_tkr_y0 + 40]
+    ctr_tkr_y0_xlim = [ctr_tkr_x0 - 20, ctr_tkr_x0 + 20]
+    ctr_tkr_z0_xlim = [ctr_tkr_x0 - 20, ctr_tkr_x0 + 20]
+
+    ctr_tkr_x0_ylim = [ctr_tkr_z0 - 40, ctr_tkr_z0 + 40]
+    ctr_tkr_y0_ylim = [ctr_tkr_z0 - 40, ctr_tkr_z0 + 40]
+    ctr_tkr_z0_ylim = [ctr_tkr_y0 - 40, ctr_tkr_y0 + 40]
+
+    XLIM = [ctr_tkr_x0_xlim, ctr_tkr_y0_xlim, ctr_tkr_z0_xlim]
+    YLIM = [ctr_tkr_x0_ylim, ctr_tkr_y0_ylim, ctr_tkr_z0_ylim]
+
+    # --------------------------------------------------------------------------
+    # create screenshots
+
+    if CREATE_SCREENSHOT is True:
+        createScreenshots(
+            SUBJECT=SUBJECT,
+            SUBJECTS_DIR=SUBJECTS_DIR,
+            INTERACTIVE=False,
+            VIEWS=[("x", ctr_tkr_x0), ("y", ctr_tkr_y0), ("z", ctr_tkr_z0)],
+            LAYOUT=(1, 3),
+            BASE=[os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")],
+            OVERLAY=[
+                os.path.join(
+                    SUBJECTS_DIR,
+                    SUBJECT,
+                    "mri",
+                    HEMI + ".hippoAmygLabels-" + LABEL + ".FSvoxelSpace.mgz",
+                )
+            ],
+            SURF=None,
+            OUTFILE=SCREENSHOTS_OUTFILE,
+            ORIENTATION=SCREENSHOTS_ORIENTATION,
+            XLIM=XLIM,
+            YLIM=YLIM,
+        )
diff --git a/fsqc/evaluateHypothalamicSegmentation.py b/fsqc/evaluateHypothalamicSegmentation.py
new file mode 100644
index 0000000..f578132
--- /dev/null
+++ b/fsqc/evaluateHypothalamicSegmentation.py
@@ -0,0 +1,250 @@
+"""
+This module provides a function to evaluate potential missegmentation of the hypothalamus
+
+"""
+
+# -----------------------------------------------------------------------------
+
+
+def evaluateHypothalamicSegmentation(
+    SUBJECT,
+    SUBJECTS_DIR,
+    OUTPUT_DIR,
+    CREATE_SCREENSHOT=True,
+    SCREENSHOTS_OUTFILE=[],
+    SCREENSHOTS_ORIENTATION=["radiological"],
+):
+    """
+    A function to evaluate potential missegmentation of the hypothalamus.
+
+    This script evaluates potential missegmentation of the hypothalamus as
+    created by FreeSurfer 7.2's module for hypothalamic segmentation:
+    https://surfer.nmr.mgh.harvard.edu/fswiki/HypothalamicSubunits.
+
+    If the corresponding arguments are set to 'True', the script will also
+    create screenshots. Resulting files will be saved to the same directory
+    as indicated above.
+
+    Required arguments:
+        - SUBJECT
+        - SUBJECTS_DIR
+        - OUTPUT_DIR
+
+    Optional arguments:
+        - CREATE_SCREENSHOT <bool> (default: True)
+        - SCREENSHOTS_OUTFILE <string> or empty list (default: [])
+
+    Required files:
+        - mri/hypothalamic_subunits_seg.v1.mgz
+        - mri/norm.mgz
+
+    """
+
+    # --------------------------------------------------------------------------
+    # imports
+
+    import os
+    import logging
+
+    import nibabel as nb
+    import numpy as np
+    from scipy import ndimage
+
+    from fsqc.createScreenshots import createScreenshots
+    from fsqc.fsqcUtils import binarizeImage
+
+    # --------------------------------------------------------------------------
+    # check files
+
+    if not os.path.isfile(os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")):
+        logging.error(
+            "ERROR: could not find "
+            + os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")
+            + ", not running hypothalamus module."
+        )
+
+        raise ValueError("File not found")
+
+    if not os.path.isfile(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz")
+    ):
+        logging.error(
+            "ERROR: could not find "
+            + os.path.join(
+                SUBJECTS_DIR, SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz"
+            )
+            + ", not running hypothalamus module."
+        )
+
+        raise ValueError("File not found")
+
+    if not SCREENSHOTS_OUTFILE:
+        SCREENSHOTS_OUTFILE = os.path.join(OUTPUT_DIR, "hypothalamus.png")
+
+    # --------------------------------------------------------------------------
+    # create mask
+
+    binarizeImage(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz"),
+        os.path.join(OUTPUT_DIR, "hypothalamus.mgz"),
+        match=[801, 802, 803, 804, 805, 806, 807, 808, 809, 810],
+    )
+
+    # --------------------------------------------------------------------------
+    # get centroids
+
+    seg = nb.load(
+        os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz")
+    )
+    seg_data = seg.get_fdata()
+    seg_labels = np.setdiff1d(np.unique(seg_data), 0)
+
+    centroids = np.array(ndimage.center_of_mass(seg_data, seg_data, seg_labels))
+    centroids = np.concatenate((seg_labels[:, np.newaxis], centroids), axis=1)
+
+    vox2ras_tkr = seg.header.get_vox2ras_tkr()
+
+    ctr_tkr = np.concatenate(
+        (centroids[:, 1:4], np.ones((centroids.shape[0], 1))), axis=1
+    )
+    ctr_tkr = np.matmul(vox2ras_tkr, ctr_tkr.T).T
+    ctr_tkr = np.concatenate(
+        (np.array(centroids[:, 0], ndmin=2).T, ctr_tkr[:, 0:3]), axis=1
+    )
+
+    #
+
+    ctr_tkr_x0 = ctr_tkr[3, 1]
+    ctr_tkr_x1 = ctr_tkr[8, 1]
+    ctr_tkr_y0 = (ctr_tkr[0, 2] + ctr_tkr[5, 2]) / 2
+    ctr_tkr_y1 = (ctr_tkr[1, 2] + ctr_tkr[6, 2]) / 2
+    ctr_tkr_y2 = (ctr_tkr[3, 2] + ctr_tkr[8, 2]) / 2
+    ctr_tkr_y3 = (ctr_tkr[4, 2] + ctr_tkr[9, 2]) / 2
+    ctr_tkr_y4 = (ctr_tkr[2, 2] + ctr_tkr[7, 2]) / 2
+    ctr_tkr_z0 = (ctr_tkr[2, 3] + ctr_tkr[7, 3]) / 2
+    ctr_tkr_z1 = (ctr_tkr[4, 3] + ctr_tkr[9, 3]) / 2
+
+    # set ranges for cropping the image (assuming RAS coordinates)
+
+    ctr_tkr_x0_xlim = [ctr_tkr[3, 2] - 20, ctr_tkr[3, 2] + 20]
+    ctr_tkr_x1_xlim = [ctr_tkr[8, 2] - 20, ctr_tkr[8, 2] + 20]
+
+    ctr_tkr_y0_xlim = [
+        (ctr_tkr[0, 1] + ctr_tkr[5, 1]) / 2 - 20,
+        (ctr_tkr[0, 1] + ctr_tkr[5, 1]) / 2 + 20,
+    ]
+    ctr_tkr_y1_xlim = [
+        (ctr_tkr[1, 1] + ctr_tkr[6, 1]) / 2 - 20,
+        (ctr_tkr[1, 1] + ctr_tkr[6, 1]) / 2 + 20,
+    ]
+    ctr_tkr_y2_xlim = [
+        (ctr_tkr[3, 1] + ctr_tkr[8, 1]) / 2 - 20,
+        (ctr_tkr[3, 1] + ctr_tkr[8, 1]) / 2 + 20,
+    ]
+    ctr_tkr_y3_xlim = [
+        (ctr_tkr[4, 1] + ctr_tkr[9, 1]) / 2 - 20,
+        (ctr_tkr[4, 1] + ctr_tkr[9, 1]) / 2 + 20,
+    ]
+    ctr_tkr_y4_xlim = [
+        (ctr_tkr[2, 1] + ctr_tkr[7, 1]) / 2 - 20,
+        (ctr_tkr[2, 1] + ctr_tkr[7, 1]) / 2 + 20,
+    ]
+
+    ctr_tkr_z0_xlim = [
+        (ctr_tkr[2, 1] + ctr_tkr[7, 1]) / 2 - 20,
+        (ctr_tkr[2, 1] + ctr_tkr[7, 1]) / 2 + 20,
+    ]
+    ctr_tkr_z1_xlim = [
+        (ctr_tkr[4, 1] + ctr_tkr[9, 1]) / 2 - 20,
+        (ctr_tkr[4, 1] + ctr_tkr[9, 1]) / 2 + 20,
+    ]
+
+    ctr_tkr_x0_ylim = [ctr_tkr[3, 3] - 20, ctr_tkr[3, 3] + 20]
+    ctr_tkr_x1_ylim = [ctr_tkr[8, 3] - 20, ctr_tkr[8, 3] + 20]
+
+    ctr_tkr_y0_ylim = [
+        (ctr_tkr[0, 3] + ctr_tkr[5, 3]) / 2 - 20,
+        (ctr_tkr[0, 3] + ctr_tkr[5, 3]) / 2 + 20,
+    ]
+    ctr_tkr_y1_ylim = [
+        (ctr_tkr[1, 3] + ctr_tkr[6, 3]) / 2 - 20,
+        (ctr_tkr[1, 3] + ctr_tkr[6, 3]) / 2 + 20,
+    ]
+    ctr_tkr_y2_ylim = [
+        (ctr_tkr[3, 3] + ctr_tkr[8, 3]) / 2 - 20,
+        (ctr_tkr[3, 3] + ctr_tkr[8, 3]) / 2 + 20,
+    ]
+    ctr_tkr_y3_ylim = [
+        (ctr_tkr[4, 3] + ctr_tkr[9, 3]) / 2 - 20,
+        (ctr_tkr[4, 3] + ctr_tkr[9, 3]) / 2 + 20,
+    ]
+    ctr_tkr_y4_ylim = [
+        (ctr_tkr[2, 3] + ctr_tkr[7, 3]) / 2 - 20,
+        (ctr_tkr[2, 3] + ctr_tkr[7, 3]) / 2 + 20,
+    ]
+
+    ctr_tkr_z0_ylim = [
+        (ctr_tkr[2, 2] + ctr_tkr[7, 2]) / 2 - 20,
+        (ctr_tkr[2, 2] + ctr_tkr[7, 2]) / 2 + 20,
+    ]
+    ctr_tkr_z1_ylim = [
+        (ctr_tkr[4, 2] + ctr_tkr[9, 2]) / 2 - 20,
+        (ctr_tkr[4, 2] + ctr_tkr[9, 2]) / 2 + 20,
+    ]
+
+    XLIM = [
+        ctr_tkr_x0_xlim,
+        ctr_tkr_x1_xlim,
+        ctr_tkr_y0_xlim,
+        ctr_tkr_y1_xlim,
+        ctr_tkr_y2_xlim,
+        ctr_tkr_y3_xlim,
+        ctr_tkr_y4_xlim,
+        ctr_tkr_z0_xlim,
+        ctr_tkr_z1_xlim,
+    ]
+
+    YLIM = [
+        ctr_tkr_x0_ylim,
+        ctr_tkr_x1_ylim,
+        ctr_tkr_y0_ylim,
+        ctr_tkr_y1_ylim,
+        ctr_tkr_y2_ylim,
+        ctr_tkr_y3_ylim,
+        ctr_tkr_y4_ylim,
+        ctr_tkr_z0_ylim,
+        ctr_tkr_z1_ylim,
+    ]
+
+    # --------------------------------------------------------------------------
+    # create screenshot
+
+    if CREATE_SCREENSHOT is True:
+        createScreenshots(
+            SUBJECT=SUBJECT,
+            SUBJECTS_DIR=SUBJECTS_DIR,
+            INTERACTIVE=False,
+            VIEWS=[
+                ("x", ctr_tkr_x0),
+                ("x", ctr_tkr_x1),
+                ("y", ctr_tkr_y0),
+                ("y", ctr_tkr_y1),
+                ("y", ctr_tkr_y2),
+                ("y", ctr_tkr_y3),
+                ("y", ctr_tkr_y4),
+                ("z", ctr_tkr_z0),
+                ("z", ctr_tkr_z1),
+            ],
+            LAYOUT=(1, 9),
+            BASE=[os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "norm.mgz")],
+            OVERLAY=[
+                os.path.join(
+                    SUBJECTS_DIR, SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz"
+                )
+            ],
+            SURF=None,
+            OUTFILE=SCREENSHOTS_OUTFILE,
+            ORIENTATION=SCREENSHOTS_ORIENTATION,
+            XLIM=XLIM,
+            YLIM=YLIM,
+        )
diff --git a/fsqc/fsqcMain.py b/fsqc/fsqcMain.py
new file mode 100644
index 0000000..2b837f9
--- /dev/null
+++ b/fsqc/fsqcMain.py
@@ -0,0 +1,2489 @@
+"""
+This module provides the main functionality of the fsqc package.
+
+"""
+
+# ==============================================================================
+# FUNCTIONS
+
+# ------------------------------------------------------------------------------
+# get_version()
+
+def get_version():
+
+    from importlib import metadata
+
+    try:
+        # requires existing installation
+        version = metadata.version('fsqc')
+    except Exception as e:
+        # fall-back if package is not installed, but run directly
+        version = "unknown"
+
+    return version
+
+# ------------------------------------------------------------------------------
+# get_help()
+
+
+def get_help(print_help=True, return_help=False):
+    """
+    a function to return a help message
+
+    """
+
+    HELPTEXT = """
+
+    fsqc
+
+
+    ============
+    Description:
+    ============
+
+    This is a set of quality assurance / quality control scripts for Fastsurfer-
+    or Freesurfer-processed structural MRI data.
+
+    It is a revision, extension, and translation to the Python language of the
+    original Freesurfer QA Tools that are provided at
+    https://surfer.nmr.mgh.harvard.edu/fswiki/QATools
+
+    It has been augmented by additional functions from the MRIQC toolbox, available
+    at https://github.com/poldracklab/mriqc and https://osf.io/haf97, and with code
+    derived from the shapeDNA and brainPrint toolboxes, available at
+    https://reuter.mit.edu.
+
+    The core functionality of this toolbox is to compute the following features:
+
+    - wm_snr_orig   ...  signal-to-noise for white matter in orig.mgz
+    - gm_snr_orig   ...  signal-to-noise for gray matter in orig.mgz
+    - wm_snr_norm   ...  signal-to-noise for white matter in norm.mgz
+    - gm_snr_norm   ...  signal-to-noise for gray matter in norm.mgz
+    - cc_size       ...  relative size of the corpus callosum
+    - holes_lh      ...  number of holes in the left hemisphere
+    - holes_rh      ...  number of holes in the right hemisphere
+    - defects_lh    ...  number of defects in the left hemisphere
+    - defects_rh    ...  number of defects in the right hemisphere
+    - topo_lh       ...  topological fixing time for the left hemisphere
+    - topo_rh       ...  topological fixing time for the right hemisphere
+    - con_snr_lh    ...  wm/gm contrast signal-to-noise ratio in the left hemisphere
+    - con_snr_rh    ...  wm/gm contrast signal-to-noise ratio in the right hemisphere
+    - rot_tal_x     ...  rotation component of the Talairach transform around the x axis
+    - rot_tal_y     ...  rotation component of the Talairach transform around the y axis
+    - rot_tal_z     ...  rotation component of the Talairach transform around the z axis
+
+    The program will use an existing output directory (or try to create it) and
+    write a csv table into that location. The csv table will contain the above
+    metrics plus a subject identifier.
+
+    In addition to the core functionality of the toolbox there are several optional
+    modules that can be run according to need:
+
+    - screenshots module
+
+    This module allows for the automated generation of cross-sections of the brain
+    that are overlaid with the anatomical segmentations (asegs) and the white and
+    pial surfaces. These images will be saved to the 'screenshots' subdirectory
+    that will be created within the output directory. These images can be used for
+    quickly glimpsing through the processing results. Note that no display manager
+    is required for this module, i.e. it can be run on a remote server, for example.
+
+    - surfaces module
+
+    This module allows for the automated generation of surface renderings of the
+    left and right pial and inflated surfaces, overlaid with the aparc annotation.
+    These images will be saved to the 'surfaces' subdirectory that will be created
+    within the output directory.
+
+    - skullstrip module
+
+    This module allows for the automated generation cross-sections of the brain
+    that are overlaid with the colored and semi-transparent brainmask. this allows
+    to check the quality of the skullstripping in FreeSurfer. The resulting images
+    will be saved to the 'skullstrip' subdirectory that will be created within the
+    output directory.
+
+    - fornix module
+
+    This is a module to assess potential issues with the segmentation of the
+    corpus callosum, which may incorrectly include parts of the fornix. To assess
+    segmentation quality, a screenshot of the contours of the corpus callosum
+    segmentation overlaid on the norm.mgz will be saved in the 'fornix'
+    subdirectory of the output directory.
+
+    - modules for the amygdala, hippocampus, and hypothalamus
+
+    These modules evaluate potential missegmentations of the amygdala, hippocampus,
+    and hypothalamus. To assess segmentation quality, screenshots will be created
+    These modules require prior processing of the MR images with FreeSurfer's
+    dedicated toolboxes for the segmentation of the amygdala and hippocampus, and
+    the hypothalamus, respectively.
+
+    - shape module
+
+    The shape module will run a shapeDNA / brainprint analysis to compute distances
+    of shape descriptors between lateralized brain structures. This can be used
+    to identify discrepancies and irregularities between pairs of corresponding
+    structures. The results will be included in the main csv table, and the output
+    directory will also contain a "brainprint" subdirectory.
+
+    - outlier module
+
+    This is a module to detect extreme values among the subcortical ('aseg')
+    segmentations as well as the cortical parcellations ('aparc'). If present,
+    hypothalamic and hippocampal subsegmentations will also be included.
+
+    The outlier detection is based on comparisons with the distributions of the
+    sample as well as normative values taken from the literature (see References).
+
+    For comparisons with the sample distributions, extreme values are defined in
+    two ways: nonparametrically, i.e. values that are 1.5 times the interquartile
+    range below or above the 25th or 75th percentile of the sample, respectively,
+    and parametrically, i.e. values that are more than 2 standard deviations above
+    or below the sample mean. Note that a minimum of 5 supplied subjects is
+    required for running these analyses, otherwise `NaNs` will be returned.
+
+    For comparisons with the normative values, lower and upper bounds are computed
+    from the 95% prediction intervals of the regression models given in Potvin et
+    al., 1996, and values exceeding these bounds will be flagged. As an
+    alternative, users may specify their own normative values by using the
+    '--outlier-table' argument. This requires a custom csv table with headers
+    `label`, `upper`, and `lower`, where `label` indicates a column of anatomical
+    names. It can be a subset and the order is arbitrary, but naming must exactly
+    match the nomenclature of the 'aseg.stats' and/or '[lr]h.aparc.stats' file.
+    If cortical parcellations are included in the outlier table for a comparison
+    with aparc.stats values, the labels must have a 'lh.' or 'rh.' prefix.
+    `upper` and `lower` are user-specified upper and lower bounds.
+
+    The main csv table will be appended with the following summary variables, and
+    more detailed output about will be saved as csv tables in the 'outliers'
+    subdirectory of the main output directory.
+
+    n_outliers_sample_nonpar ... number of structures that are 1.5 times the IQR
+                                 above/below the 75th/25th percentile
+    n_outliers_sample_param  ... number of structures that are 2 SD above/below
+                                 the mean
+    n_outliers_norms         ... number of structures exceeding the upper and
+                                 lower bounds of the normative values
+
+
+    ======
+    Usage:
+    ======
+
+        run_fsqc --subjects_dir <directory> --output_dir <directory>
+                                  [--subjects SubjectID [SubjectID ...]]
+                                  [--subjects-file <file>]
+                                  [--screenshots] [--screenshots-html]
+                                  [--surfaces] [--surfaces-html]
+                                  [--skullstrip] [--skullstrip-html]
+                                  [--fornix] [--fornix-html] [--hypothalamus]
+                                  [--hypothalamus-html] [--hippocampus]
+                                  [--hippocampus-html] [--hippocampus-label <label>]
+                                  [--shape] [--outlier] [--fastsurfer] [-h]
+
+        required arguments:
+          --subjects_dir <directory>
+                                subjects directory with a set of Freesurfer- or
+                                Fastsurfer-processed individual datasets.
+          --output_dir <directory>
+                                output directory
+
+        optional arguments:
+          --subjects SubjectID [SubjectID ...]
+                                list of subject IDs
+          --subjects-file <file>
+                                filename of a file with subject IDs (one per line)
+          --screenshots         create screenshots of individual brains
+          --screenshots-html    create screenshots of individual brains and
+                                html summary page
+          --surfaces            create screenshots of individual brain surfaces
+          --surfaces-html       create screenshots of individual brain surfaces
+                                and html summary page
+          --skullstrip          create screenshots of individual brainmasks
+          --skullstrip-html     create screenshots of individual brainmasks and
+                                html summary page
+          --fornix              check fornix segmentation
+          --fornix-html         check fornix segmentation and create html summary
+                                page of fornix evaluation
+          --hypothalamus        check hypothalamic segmentation
+          --hypothalamus-html   check hypothalamic segmentation and create html
+                                summary page
+          --hippocampus         check segmentation of hippocampus and amygdala
+          --hippocampus-html    check segmentation of hippocampus and amygdala
+                                and create html summary page
+          --hippocampus-label   specify label for hippocampus segmentation files
+                                (default: None). The full filename is then
+                                [lr]h.hippoAmygLabels-<LABEL>.FSvoxelSpace.mgz
+          --shape               run shape analysis
+          --outlier             run outlier detection
+          --outlier-table       specify normative values (only in conjunction with
+                                --outlier)
+          --fastsurfer          use FastSurfer instead of FreeSurfer output
+          --exit-on-error       terminate the program when encountering an error;
+                                otherwise, try to continue with the next module or
+                                case
+
+        getting help:
+          -h, --help            display this help message and exit
+          --more-help           display extensive help message and exit
+
+        expert options:
+          --screenshots_base <image>
+                                filename of an image that should be used instead of
+                                norm.mgz as the base image for the screenshots. Can be
+                                an individual file (which would not be appropriate for
+                                multi-subject analysis) or can be a file without
+                                pathname and with the same filename across subjects
+                                within the 'mri' subdirectory of an individual FreeSurfer
+                                results directory (which would be appropriate for multi-
+                                subject analysis).
+          --screenshots_overlay <image>
+                                path to an image that should be used instead of
+                                aseg.mgz as the overlay image for the screenshots;
+                                can also be none. Can be an individual file (which would
+                                not be appropriate for multi-subject analysis) or can be
+                                a file without pathname and with the same filename across
+                                subjects within the 'mri' subdirectory of an individual
+                                FreeSurfer results directory (which would be appropriate
+                                for multi-subject analysis).
+          --screenshots_surf <surf> [<surf> ...]
+                                one or more surface files that should be used instead
+                                of [lr]h.white and [lr]h.pial; can also be none.
+                                Can be one or more individual file(s) (which would not be
+                                appropriate for multi-subject analysis) or can be a (list
+                                of) file(s) without pathname and with the same filename
+                                across subjects within the 'surf' subdirectory of an
+                                individual FreeSurfer results directory (which would be
+                                appropriate for multi-subject analysis).
+          --screenshots_views <view> [<view> ...]
+                                one or more views to use for the screenshots in
+                                the form of x=<numeric> y=<numeric> and/or
+                                z=<numeric>. order does not matter. default views
+                                are x=-10 x=10 y=0 z=0.
+          --screenshots_layout <rows> <columns>
+                                layout matrix for screenshot images
+
+
+    ========================
+    Use as a python package:
+    ========================
+
+    As an alternative to their command-line usage, the fsqc scripts can also be run
+    within a pure Python environment, i.e. installed and imported as a Python package.
+
+    Use `import fsqc` (or sth. equivalent) to import the package within a
+    Python environment, and use the `run_fsqc` function from the `fsqc` module to
+    run an analysis:
+
+    ```python
+    import fsqc
+    fsqc.run_fsqc(subjects_dir='/my/subjects/dir', output_dir='/my/output/dir')
+    ```
+
+    See `fsqc.get_help()` for further usage info and additional options.
+
+
+    =============
+    Requirements:
+    =============
+
+    At least one subject whose structural MR image was processed with Freesurfer
+    6.0 or later, or FastSurfer v1.1 or later (including the surface pipeline).
+
+    A Python version >= 3.8 is required to run this script.
+
+    Required packages include (among others) the nibabel and skimage package for
+    the core functionality, plus the the matplotlib, pandas, and transform3d
+    packages for some optional functions and modules. See the `requirements.txt`
+    file for a complete list. Use `pip3 install -r requirements.txt` to install
+    these packages.
+
+    This software has been tested on Ubuntu 20.04.
+
+    A working [FreeSurfer](https://freesurfer.net) installation (version 6 or
+    newer) is required for running the 'shape' module of this toolbox. Also make
+    sure that FreeSurfer is sourced (i.e., `FREESURFER_HOME` is set as an
+    environment variable) before running an analysis.
+
+    =============
+    Known Issues:
+    =============
+
+    - Aborted / restarted recon-all runs: the program will analyze recon-all
+      logfiles, and may fail or return erroneous results if the logfile is
+      appended by multiple restarts of recon-all runs. Ideally, the logfile should
+      therefore consist of just a single, successful recon-all run.
+    - High-resolution data: Prior to update v1.4, the screenshots and fornix module
+      did not work well with high-resolution data that was processed using the
+      -cm flag in recon-all. With update v1.4 this has been fixed for the
+      screenhots module, but the fornix module is still experimental for
+      high-resolution data.
+
+
+    ========
+    Authors:
+    ========
+
+    - fsqc: Kersten Diers, Tobias Wolff, and Martin Reuter.
+    - Freesurfer QA Tools: David Koh, Stephanie Lee, Jenni Pacheco, Vasanth Pappu,
+      and Louis Vinke.
+    - shapeDNA and brainPrint toolboxes: Martin Reuter
+
+
+    ===========
+    References:
+    ===========
+
+    Esteban O, Birman D, Schaer M, Koyejo OO, Poldrack RA, Gorgolewski KJ; MRIQC:
+    Advancing the Automatic Prediction of Image Quality in MRI from Unseen Sites;
+    PLOS ONE 12(9):e0184661; doi:10.1371/journal.pone.0184661.
+
+    Wachinger C, Golland P, Kremen W, Fischl B, Reuter M; 2015; BrainPrint: a
+    Discriminative Characterization of Brain Morphology; Neuroimage: 109, 232-248;
+    doi:10.1016/j.neuroimage.2015.01.032.
+
+    Reuter M, Wolter FE, Shenton M, Niethammer M; 2009; Laplace-Beltrami
+    Eigenvalues and Topological Features of Eigenfunctions for Statistical Shape
+    Analysis; Computer-Aided Design: 41, 739-755, doi:10.1016/j.cad.2009.02.007.
+
+    Potvin O, Mouiha A, Dieumegarde L, Duchesne S, & Alzheimer's Disease Neuroimaging
+    Initiative; 2016; Normative data for subcortical regional volumes over the lifetime
+    of the adult human brain; Neuroimage: 137, 9-20;
+    doi.org/10.1016/j.neuroimage.2016.05.016
+
+
+    ========
+    License:
+    ========
+
+    This software is licensed under the MIT License, see associated LICENSE file
+    for details.
+
+    Copyright (c) 2019 Image Analysis Group, DZNE e.V.
+
+    """
+
+    if print_help:
+        print(HELPTEXT)
+
+    if return_help:
+        return HELPTEXT
+
+
+# ------------------------------------------------------------------------------
+# parse_arguments
+
+
+def _parse_arguments():
+    """
+    an internal function to parse input arguments
+
+    """
+
+    # imports
+    import argparse
+    import sys
+
+    # parse
+    parser = argparse.ArgumentParser(
+        description="""
+        This program takes existing Freesurfer or Fastsurfer analysis results of
+        one or more subjects and computes a set of quality metrics. These will
+        be reported in a summary csv table.
+
+        For a description of these metrics, see the gitlab/github page or the
+        header section of this script.
+
+        Further modules are available to produce graphical outputs.
+        """,
+        add_help=False,
+        formatter_class=argparse.RawTextHelpFormatter,
+    )
+
+    required = parser.add_argument_group("required arguments")
+    required.add_argument(
+        "--subjects_dir",
+        dest="subjects_dir",
+        help="subjects directory with a set of Freesurfer processed individual datasets.",
+        metavar="<directory>",
+        required=True,
+    )
+    required.add_argument(
+        "--output_dir", dest="output_dir", help="output directory", metavar="<directory>", required=True
+    )
+
+    optional = parser.add_argument_group("optional arguments")
+    optional.add_argument(
+        "--subjects",
+        dest="subjects",
+        help="list of subject IDs. If omitted, all suitable sub-directories within the subjects directory will be used.",
+        default=None,
+        nargs="+",
+        metavar="SubjectID",
+        required=False,
+    )
+    optional.add_argument(
+        "--subjects-file",
+        dest="subjects_file",
+        help="filename with list of subject IDs (one per line). If omitted, all suitable sub-directories within the subjects directory will be used.",
+        default=None,
+        metavar="<filename>",
+        required=False,
+    )
+    optional.add_argument(
+        "--shape", dest="shape", help="run shape analysis", default=False, action="store_true", required=False
+    )
+    optional.add_argument(
+        "--screenshots",
+        dest="screenshots",
+        help="create screenshots of individual brains",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--screenshots-html",
+        dest="screenshots_html",
+        help="create screenshots of individual brains with html summary page",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--surfaces",
+        dest="surfaces",
+        help="create surface plots of individual brains",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--surfaces-html",
+        dest="surfaces_html",
+        help="create surface plots of individual brains with html summary page",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--surfaces_views",
+        dest="surfaces_views",
+        help="Specify camera views for surface images. Choose from: anterior, posterior, left, right, superior, inferior",
+        default=["left", "right", "superior", "inferior"],
+        type=str,
+        nargs="+",
+        required=False,
+    )
+    optional.add_argument(
+        "--skullstrip",
+        dest="skullstrip",
+        help="create brainmask plots of individual brains",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--skullstrip-html",
+        dest="skullstrip_html",
+        help="create brainmask plots of individual brains with html summary page",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--fornix", dest="fornix", help="check fornix segmentation", default=False, action="store_true", required=False
+    )
+    optional.add_argument(
+        "--fornix-html",
+        dest="fornix_html",
+        help="check fornix segmentation and create html summary page",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--hypothalamus",
+        dest="hypothalamus",
+        help="check hypothalamus segmentation",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--hypothalamus-html",
+        dest="hypothalamus_html",
+        help="check hypothalamus segmentation and create html summary page for evaluation",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--hippocampus",
+        dest="hippocampus",
+        help="check hippocampus segmentation",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--hippocampus-html",
+        dest="hippocampus_html",
+        help="check hippocampus segmentation and create html summary page for evaluation",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--hippocampus-label",
+        dest="hippocampus_label",
+        help="specify custom label for hippocampal segmentation files",
+        default=None,
+        metavar="<string>",
+        required=False,
+    )
+    optional.add_argument(
+        "--outlier", dest="outlier", help="run outlier detection", default=False, action="store_true", required=False
+    )
+    optional.add_argument(
+        "--outlier-table",
+        dest="outlier_table",
+        help="specify normative values",
+        default=None,
+        metavar="<filename>",
+        required=False,
+    )
+    optional.add_argument(
+        "--fastsurfer",
+        dest="fastsurfer",
+        help="use FastSurfer output",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+    optional.add_argument(
+        "--exit-on-error",
+        dest="exit_on_error",
+        help="terminate the program when encountering an error",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+
+    expert = parser.add_argument_group("expert arguments")
+    expert.add_argument(
+        "--screenshots_base",
+        dest="screenshots_base",
+        help="base image for screenshots",
+        default="default",
+        metavar="<base image for screenshots>",
+        required=False,
+    )
+    expert.add_argument(
+        "--screenshots_overlay",
+        dest="screenshots_overlay",
+        help="overlay image for screenshots",
+        default="default",
+        metavar="<overlay image for screenshots>",
+        required=False,
+    )
+    expert.add_argument(
+        "--screenshots_surf",
+        dest="screenshots_surf",
+        help="surface(s) for screenshots",
+        default="default",
+        nargs="+",
+        metavar="<surface(s) for screenshots>",
+        required=False,
+    )
+    expert.add_argument(
+        "--screenshots_views",
+        dest="screenshots_views",
+        help="view specification for screenshots",
+        default=["x=-10", "x=10", "y=0", "z=0"],
+        nargs="+",
+        metavar="<dimension=coordinate [dimension=coordinate]>",
+        required=False,
+    )
+    expert.add_argument(
+        "--screenshots_layout",
+        dest="screenshots_layout",
+        help="layout for screenshots",
+        default=["1", "4"],
+        nargs=2,
+        metavar="<num>",
+        required=False,
+    )
+    expert.add_argument(
+        "--screenshots_orientation",
+        dest="screenshots_orientation",
+        help=argparse.SUPPRESS,
+        default=["radiological"],
+        nargs=1,
+        metavar="<neurological|radiological>",
+        required=False,
+    )  # this is currently a hidden "expert" option
+
+    help = parser.add_argument_group("getting help")
+    help.add_argument("-h", "--help", help="display this help message and exit", action="help")
+    help.add_argument(
+        "--more-help",
+        dest="more_help",
+        help="display extensive help message and exit",
+        default=False,
+        action="store_true",
+        required=False,
+    )
+
+    # check if there are any inputs; if not, print help and exit
+    if len(sys.argv) == 1:
+        args = parser.parse_args(["--help"])
+    elif len(sys.argv) == 2 and sys.argv[1] == "--more-help":
+        get_help()
+        return None
+    else:
+        args = parser.parse_args()
+
+    # check for extensive help (if it exists among other arguments)
+    if args.more_help:
+        get_help()
+        return None
+
+    # prepare output
+    argsDict = dict()
+    argsDict["subjects_dir"] = args.subjects_dir
+    argsDict["output_dir"] = args.output_dir
+    argsDict["subjects"] = args.subjects
+    argsDict["subjects_file"] = args.subjects_file
+    argsDict["shape"] = args.shape
+    argsDict["screenshots"] = args.screenshots
+    argsDict["screenshots_html"] = args.screenshots_html
+    argsDict["screenshots_base"] = args.screenshots_base
+    argsDict["screenshots_overlay"] = args.screenshots_overlay
+    argsDict["screenshots_surf"] = args.screenshots_surf
+    argsDict["screenshots_views"] = args.screenshots_views
+    argsDict["screenshots_layout"] = args.screenshots_layout
+    argsDict["screenshots_orientation"] = args.screenshots_orientation
+    argsDict["surfaces"] = args.surfaces
+    argsDict["surfaces_html"] = args.surfaces_html
+    argsDict["surfaces_views"] = args.surfaces_views
+    argsDict["skullstrip"] = args.skullstrip
+    argsDict["skullstrip_html"] = args.skullstrip_html
+    argsDict["fornix"] = args.fornix
+    argsDict["fornix_html"] = args.fornix_html
+    argsDict["hypothalamus"] = args.hypothalamus
+    argsDict["hypothalamus_html"] = args.hypothalamus_html
+    argsDict["hippocampus"] = args.hippocampus
+    argsDict["hippocampus_html"] = args.hippocampus_html
+    argsDict["hippocampus_label"] = args.hippocampus_label
+    argsDict["outlier"] = args.outlier
+    argsDict["outlier_table"] = args.outlier_table
+    argsDict["fastsurfer"] = args.fastsurfer
+    argsDict["exit_on_error"] = args.exit_on_error
+
+    #
+    return argsDict
+
+
+# ------------------------------------------------------------------------------
+# check arguments
+
+
+def _check_arguments(argsDict):
+    """
+    an internal function to check input arguments
+
+    """
+
+    # --------------------------------------------------------------------------
+    # imports
+
+    import os
+    import tempfile
+    import logging
+    import warnings
+
+    logging.captureWarnings(True)
+
+    # --------------------------------------------------------------------------
+    # check arguments
+    # check if subject directory exists
+    if os.path.isdir(argsDict["subjects_dir"]):
+        logging.info("Found subjects directory " + argsDict["subjects_dir"])
+    else:
+        raise FileNotFoundError("ERROR: subjects directory " + argsDict["subjects_dir"] + " is not an existing directory")
+
+    # check if output directory exists or can be created and is writable
+    # -> this is now done during start_logging()
+
+    # check if both subjects and subjects-file were specified
+    if argsDict["subjects"] is not None and argsDict["subjects_file"] is not None:
+        raise ValueError("ERROR: Use either --subjects or --subjects-file (but not both).")
+
+    # check if subjects-file exists and get data
+    if argsDict["subjects_file"] is not None:
+        if os.path.isfile(argsDict["subjects_file"]):
+            # read file
+            with open(argsDict["subjects_file"]) as subjects_file_f:
+                argsDict["subjects"] = subjects_file_f.read().splitlines()
+        else:
+            raise FileNotFoundError("ERROR: Could not find subjects file", argsDict["subjects_file"])
+
+
+    # if neither subjects nor subjects_file are given, get contents of the subject
+    # directory and check if aseg.stats (as a proxy) exists
+    if argsDict["subjects"] is None and argsDict["subjects_file"] is None:
+        argsDict["subjects"] = []
+        for subject in os.listdir(argsDict["subjects_dir"]):
+            path_aseg_stat = os.path.join(argsDict["subjects_dir"], subject, "stats", "aseg.stats")
+            if os.path.isfile(path_aseg_stat):
+                logging.info("Found subject " + subject)
+                argsDict["subjects"].extend([subject])
+
+    # check if screenshots subdirectory exists or can be created and is writable
+    if argsDict["screenshots"] is True or argsDict["screenshots_html"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "screenshots")):
+            logging.info("Found screenshots directory " + os.path.join(argsDict["output_dir"], "screenshots"))
+        else:
+            try:
+                os.mkdir(os.path.join(argsDict["output_dir"], "screenshots"))
+            except Exception as e:
+                logging.error("ERROR: cannot create screenshots directory " + os.path.join(argsDict["output_dir"], "screenshots"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "screenshots"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "screenshots") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check screenshots_base
+    argsDict["screenshots_base"] = [argsDict["screenshots_base"]]
+
+    # check screenshots_overlay (this is either 'default' or 'none' or a single file or a list; further checks prior to execution of the screenshots module)
+    if argsDict["screenshots_overlay"].lower() == "none":
+        argsDict["screenshots_overlay"] = None
+        logging.info("Found screenshot overlays set to None")
+    else:
+        argsDict["screenshots_overlay"] = [argsDict["screenshots_overlay"]]
+
+    # check screenshots_surf (this is either 'default' or 'none' or a single file or a list; further checks prior to execution of the screenshots module)
+    if not isinstance(argsDict["screenshots_surf"], list):
+        argsDict["screenshots_surf"] = [argsDict["screenshots_surf"]]
+    if argsDict["screenshots_surf"][0].lower() == "none":
+        argsDict["screenshots_surf"] = None
+        logging.info("Found screenshot surfaces set to None")
+
+    # check if screenshots_views argument can be evaluated
+    if argsDict["screenshots_views"] == "default":
+        argsDict["screenshots_views"] = [argsDict["screenshots_views"]]
+    else:
+        for x in argsDict["screenshots_views"]:
+            isXYZ = x.split("=")[0] == "x" or x.split("=")[0] == "y" or x.split("=")[0] == "z"
+            try:
+                int(x.split("=")[1])
+                isConvertible = True
+            except Exception:
+                isConvertible = False
+            if not isXYZ or not isConvertible:
+                logging.error("ERROR: could not understand " + x)
+                logging.error("       the --screenshots_views argument can only contain one or more x=<numeric> y=<numeric> z=<numeric> expressions.")
+                logging.error("       for example: --screenshots_views x=0")
+                logging.error("                    --screenshots_views x=-10 x=10 y=0")
+                logging.error("                    --screenshots_views x=0 z=0")
+                raise ValueError
+
+        logging.info("Found screenshot coordinates " + ' '.join(argsDict["screenshots_views"]))
+        argsDict["screenshots_views"] = [
+            (y[0], int(y[1])) for y in [x.split("=") for x in argsDict["screenshots_views"]]
+        ]
+
+    # check screenshots_layout
+    if argsDict["screenshots_layout"] is not None:
+        if all([x.isdigit() for x in argsDict["screenshots_layout"]]):
+            argsDict["screenshots_layout"] = [int(x) for x in argsDict["screenshots_layout"]]
+        else:
+            raise TypeError("ERROR: screenshots_layout argument can only contain integer numbers.")
+
+    # check screenshots_orientation
+    if argsDict["screenshots_orientation"] != ["neurological"] and argsDict["screenshots_orientation"] != ["radiological"]:
+        raise TypeError("ERROR: screenshots_orientation argument must be either 'neurological' or 'radiological'.")
+    else:
+        logging.info("Found screenshot orientation set to " + argsDict["screenshots_orientation"][0])
+
+    # check if skullstrip subdirectory exists or can be created and is writable
+    if argsDict["skullstrip"] is True or argsDict["skullstrip_html"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "skullstrip")):
+            logging.info("Found skullstrip directory " + os.path.join(argsDict["output_dir"], "skullstrip"))
+        else:
+            try:
+                os.mkdir(os.path.join(argsDict["output_dir"], "skullstrip"))
+            except Exception as e:
+                logging.error("ERROR: cannot create skullstrip directory " + os.path.join(argsDict["output_dir"], "skullstrip"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "skullstrip"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "skullstrip") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check if fornix subdirectory exists or can be created and is writable
+    if argsDict["fornix"] is True or argsDict["fornix_html"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "fornix")):
+            logging.info("Found fornix directory " + os.path.join(argsDict["output_dir"], "fornix"))
+        else:
+            try:
+                os.mkdir(os.path.join(argsDict["output_dir"], "fornix"))
+            except Exception as e:
+                logging.error("ERROR: cannot create fornix directory " + os.path.join(argsDict["output_dir"], "fornix"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "fornix"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "fornix") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check if hypothalamus subdirectory exists or can be created and is writable
+    if argsDict["hypothalamus"] is True or argsDict["hypothalamus_html"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "hypothalamus")):
+            logging.info("Found hypothalamus directory " + os.path.join(argsDict["output_dir"], "hypothalamus"))
+        else:
+            try:
+                os.mkdir(os.path.join(argsDict["output_dir"], "hypothalamus"))
+            except Exception as e:
+                logging.error("ERROR: cannot create hypothalamus directory " + os.path.join(argsDict["output_dir"], "hypothalamus"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "hypothalamus"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "hypothalamus") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check if hippocampus subdirectory exists or can be created and is writable
+    if argsDict["hippocampus"] is True or argsDict["hippocampus_html"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "hippocampus")):
+            logging.info("Found hippocampus directory " + os.path.join(argsDict["output_dir"], "hippocampus"))
+        else:
+            try:
+                os.mkdir(os.path.join(argsDict["output_dir"], "hippocampus"))
+            except Exception as e:
+                logging.error("ERROR: cannot create hippocampus directory " + os.path.join(argsDict["output_dir"], "hippocampus"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "hippocampus"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "hippocampus") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check if label file is given
+    if (argsDict["hippocampus"] is True or argsDict["hippocampus_html"] is True) and argsDict["hippocampus_label"] is None:
+        logging.error("ERROR: The --hippocampus-label <LABEL> argument must be specified if using --hippocampus or --hippocampus-html")
+        logging.error("       The filename of the segmentation file must correspond to [lr]h.hippoAmygLabels-<LABEL>.FSvoxelSpace.mgz")
+        raise ValueError
+
+    # check if shape subdirectory exists or can be created and is writable
+    if argsDict["shape"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "brainprint")):
+            logging.info("Found brainprint directory " + os.path.join(argsDict["output_dir"], "brainprint"))
+        else:
+            try:
+                os.makedirs(os.path.join(argsDict["output_dir"], "brainprint"))
+            except Exception as e:
+                logging.error("ERROR: cannot create brainprint directory " + os.path.join(argsDict["output_dir"], "brainprint"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "brainprint"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "brainprint") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check if outlier subdirectory exists or can be created and is writable
+    if argsDict["outlier"] is True:
+        if os.path.isdir(os.path.join(argsDict["output_dir"], "outliers")):
+            logging.info("Found outliers directory " + os.path.join(argsDict["output_dir"], "outliers"))
+        else:
+            try:
+                os.makedirs(os.path.join(argsDict["output_dir"], "outliers"))
+            except Exception as e:
+                logging.error("ERROR: cannot create outliers directory " + os.path.join(argsDict["output_dir"], "outliers"))
+                logging.error("Reason: " + str(e))
+                raise
+
+            try:
+                testfile = tempfile.TemporaryFile(dir=os.path.join(argsDict["output_dir"], "outliers"))
+                testfile.close()
+            except Exception as e:
+                logging.error("ERROR: " + os.path.join(argsDict["output_dir"], "outliers") + " not writeable")
+                logging.error("Reason: " + str(e))
+                raise
+
+    # check if outlier-table exists if it was given, otherwise exit
+    if argsDict["outlier_table"] is not None:
+        if os.path.isfile(argsDict["outlier_table"]):
+            logging.info("Found table with normative values " + argsDict["outlier_table"])
+        else:
+            raise FileNotFoundError("ERROR: Could not find table with normative values ", argsDict["outlier_table"])
+
+    # check for required files
+    subjects_to_remove = list()
+    for subject in argsDict["subjects"]:
+        # -files: stats/aseg.stats
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "stats", "aseg.stats")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        # -files: surf/[lr]h.w-g.pct.mgh, label/[lr]h.cortex.label
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "lh.w-g.pct.mgh")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "rh.w-g.pct.mgh")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "label", "lh.cortex.label")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "label", "rh.cortex.label")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        # -files: mri/transforms/talairach.lta
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "transforms", "talairach.lta")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        # -files: mri/norm.mgz, mri/aseg.mgz, mri/aparc+aseg.mgz for FreeSurfer
+        # -files: mri/norm.mgz, mri/aseg.mgz, mri/aparc.DKTatlas+aseg.deep.mgz for FastSurfer
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "norm.mgz")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "aseg.mgz")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        if argsDict["fastsurfer"] is True:
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "aparc.DKTatlas+aseg.deep.mgz")
+        else:
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "aparc+aseg.mgz")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        # -files: scripts/recon-all.log
+        path_check = os.path.join(argsDict["subjects_dir"], subject, "scripts", "recon-all.log")
+        if not os.path.isfile(path_check):
+            warnings.warn("Could not find " + path_check + " for subject " + subject)
+            subjects_to_remove.extend([subject])
+
+        # check screenshots
+        if (argsDict["screenshots"] is True or argsDict["screenshots_html"] is True) and argsDict[
+            "screenshots_surf"
+        ] == ["default"]:
+            # -files: surf/[lr]h.white (optional), surf/[lr]h.pial (optional)
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "lh.white")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "rh.white")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "lh.pial")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "rh.pial")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+        # check surfaces
+        if argsDict["surfaces"] is True or argsDict["surfaces_html"] is True:
+            # -files: surf/[lr]h.white (optional), surf/[lr]h.inflated (optional), label/[lr]h.aparc.annot (optional) for freesurfer
+            # -files: surf/[lr]h.white (optional), surf/[lr]h.inflated (optional), label/[lr]h.aparc.DKTatlas.annot (optional) for fastsurfer
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "lh.inflated")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "rh.inflated")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "lh.pial")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "surf", "rh.pial")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            if argsDict["fastsurfer"] is True:
+                path_check = os.path.join(argsDict["subjects_dir"], subject, "label", "lh.aparc.DKTatlas.annot")
+            else:
+                path_check = os.path.join(argsDict["subjects_dir"], subject, "label", "lh.aparc.annot")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            if argsDict["fastsurfer"] is True:
+                path_check = os.path.join(argsDict["subjects_dir"], subject, "label", "rh.aparc.DKTatlas.annot")
+            else:
+                path_check = os.path.join(argsDict["subjects_dir"], subject, "label", "rh.aparc.annot")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+        if len(argsDict["surfaces_views"]) > 0:
+            _views_available = ["anterior", "posterior", "left", "right", "superior", "inferior"]
+            for v in argsDict["surfaces_views"].copy():
+                if v not in _views_available:
+                    logging.error(f"ERROR: Skip unexpected view for surface plots: {v}")
+                    argsDict["surfaces_views"].remove(v)
+
+        # check skullstrip
+        if argsDict["skullstrip"] is True or argsDict["skullstrip_html"] is True:
+            # -files: surf/[lr]h.white (optional), surf/[lr]h.inflated (optional), label/[lr]h.aparc.annot (optional)
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "orig.mgz")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "brainmask.mgz")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+        # check fornix
+        if argsDict["fornix"] is True or argsDict["fornix_html"] is True:
+            # -files: mri/transforms/cc_up.lta
+            path_check = os.path.join(argsDict["subjects_dir"], subject, "mri", "transforms", "cc_up.lta")
+            if not os.path.isfile(path_check):
+                warnings.warn("Could not find " + path_check + " for subject " + subject)
+                subjects_to_remove.extend([subject])
+
+    # remove subjects with missing files after creating unique list
+    [argsDict["subjects"].remove(x) for x in list(set(subjects_to_remove))]
+
+    # check if we have any subjects after all
+    if not argsDict["subjects"]:
+        raise ValueError("ERROR: no subjects to process")
+
+    # now return
+    return argsDict
+
+
+# ------------------------------------------------------------------------------
+# check packages
+
+
+def _check_packages():
+    """
+    an internal function to check required / recommended packages
+
+    """
+
+    import importlib.util
+    import packaging.version
+    import sys
+
+    if sys.version_info <= (3, 8):
+        raise RuntimeError("ERROR: Python version must be 3.8 or greater\n")
+
+    if importlib.util.find_spec("skimage") is None:
+        raise ImportError("ERROR: the 'skimage' package is required for running this script, please install.\n")
+
+    if importlib.util.find_spec("pandas") is None:
+        raise ImportError("ERROR: the 'pandas' package is required for running this script, please install.\n")
+
+    if importlib.util.find_spec("matplotlib") is None:
+        raise ImportError("ERROR: the 'matplotlib' package is required for running this script, please install.\n")
+
+    if importlib.util.find_spec("lapy") is not None:
+        import lapy as lp
+        if not hasattr(lp, "__version__"):
+            raise ImportError("ERROR: Could not determine version of the 'lapy' package (see README.md for details on installation)")
+        elif packaging.version.parse(lp.__version__) < packaging.version.parse("1.0"):
+            raise ImportError("ERROR: A version >=1.0 of the 'lapy' package is required for surface plots (see README.md for details on installation)")
+    else:
+        raise ImportError("ERROR: Could not find the 'lapy' package (see README.md for details on installation)")
+
+    if importlib.util.find_spec("brainprint") is None:
+        raise ImportError("ERROR: could not import the brainprint package, is it installed?")
+
+
+# ------------------------------------------------------------------------------
+# do fsqc
+
+
+def _do_fsqc(argsDict):
+    """
+    an internal function to run the fsqc submodules
+
+    """
+
+    # ------------------------------------------------------------------------------
+    # imports
+
+    import csv
+    import os
+    import time
+    import logging
+
+    import numpy as np
+
+    from fsqc.checkCCSize import checkCCSize
+    from fsqc.checkContrast import checkContrast
+    from fsqc.checkRotation import checkRotation
+    from fsqc.checkSNR import checkSNR
+    from fsqc.checkTopology import checkTopology
+    from fsqc.createScreenshots import createScreenshots
+    from fsqc.createSurfacePlots import createSurfacePlots
+    from fsqc.evaluateFornixSegmentation import evaluateFornixSegmentation
+    from fsqc.evaluateHippocampalSegmentation import evaluateHippocampalSegmentation
+    from fsqc.evaluateHypothalamicSegmentation import evaluateHypothalamicSegmentation
+    from fsqc.outlierDetection import outlierDetection, outlierTable
+
+    # ------------------------------------------------------------------------------
+    # internal settings (might be turned into command-line arguments in the future)
+
+    SNR_AMOUT_EROSION = 3
+    FORNIX_SCREENSHOT = True
+    FORNIX_SHAPE = False
+    FORNIX_N_EIGEN = 15
+    HYPOTHALAMUS_SCREENSHOT = True
+    HIPPOCAMPUS_SCREENSHOT = True
+    OUTLIER_N_MIN = 5
+
+    SHAPE_EVEC = False
+    SHAPE_SKIPCORTEX = False
+    SHAPE_NUM = 50
+    SHAPE_NORM = "geometry"
+    SHAPE_REWEIGHT = True
+    SHAPE_ASYMMETRY = True
+
+    # --------------------------------------------------------------------------
+    # process
+
+    # start the processing with a message
+    print("")
+    print("-----------------------------")
+
+    # create metrics dict
+    metricsDict = dict()
+
+    # create images dict
+    imagesScreenshotsDict = dict()
+    imagesSurfacesDict = dict()
+    imagesSkullstripDict = dict()
+    imagesFornixDict = dict()
+    imagesHypothalamusDict = dict()
+    imagesHippocampusLeftDict = dict()
+    imagesHippocampusRightDict = dict()
+
+    # create status dict
+    statusDict = dict()
+
+    # loop through the specified subjects
+    for subject in argsDict["subjects"]:
+        #
+        logging.info(
+            "Starting fsqc for subject " +
+            subject +
+            " at " +
+            time.strftime("%Y-%m-%d %H:%M %Z", time.localtime(time.time())),
+        )
+
+        # ----------------------------------------------------------------------
+        # set images
+
+        if argsDict["fastsurfer"] is True:
+            aparc_image = "aparc.DKTatlas+aseg.deep.mgz"
+        else:
+            aparc_image = "aparc+aseg.mgz"
+
+        # ----------------------------------------------------------------------
+        # add subject to dictionary
+
+        metricsDict.update({subject: {"subject": subject}})
+        statusDict.update({subject: {"subject": subject}})
+
+        # ----------------------------------------------------------------------
+        # compute core metrics
+
+        # set status
+        metrics_ok = True
+
+        # get WM and GM SNR for orig.mgz
+        try:
+            wm_snr_orig, gm_snr_orig = checkSNR(
+                argsDict["subjects_dir"], subject, SNR_AMOUT_EROSION, ref_image="orig.mgz", aparc_image=aparc_image
+            )
+
+        except Exception as e:
+            logging.error("ERROR: SNR computation failed for " + subject)
+            logging.error("Reason: " + str(e))
+            wm_snr_orig = np.nan
+            gm_snr_orig = np.nan
+            metrics_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # get WM and GM SNR for norm.mgz
+        try:
+            wm_snr_norm, gm_snr_norm = checkSNR(
+                argsDict["subjects_dir"], subject, SNR_AMOUT_EROSION, ref_image="norm.mgz", aparc_image=aparc_image
+            )
+
+        except Exception as e:
+            logging.error("ERROR: SNR computation failed for " + subject)
+            logging.error("Reason: " + str(e))
+            wm_snr_norm = np.nan
+            gm_snr_norm = np.nan
+            metrics_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # check CC size
+        try:
+            cc_size = checkCCSize(argsDict["subjects_dir"], subject)
+
+        except Exception as e:
+            logging.error("ERROR: CC size computation failed for " + subject)
+            logging.error("Reason: " + str(e))
+            cc_size = np.nan
+            metrics_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # check topology
+        try:
+            holes_lh, holes_rh, defects_lh, defects_rh, topo_lh, topo_rh = checkTopology(argsDict["subjects_dir"], subject)
+
+        except Exception as e:
+            logging.error("ERROR: Topology check failed for " + subject)
+            logging.error("Reason: " + str(e))
+            holes_lh = np.nan
+            holes_rh = np.nan
+            defects_lh = np.nan
+            defects_rh = np.nan
+            topo_lh = np.nan
+            topo_rh = np.nan
+            metrics_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # check contrast
+        try:
+            con_snr_lh, con_snr_rh = checkContrast(argsDict["subjects_dir"], subject)
+
+        except Exception as e:
+            logging.error("ERROR: Contrast check failed for " + subject)
+            logging.error("Reason: " + str(e))
+            con_snr_lh = np.nan
+            con_snr_rh = np.nan
+            metrics_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # check rotation
+        try:
+            rot_tal_x, rot_tal_y, rot_tal_z = checkRotation(argsDict["subjects_dir"], subject)
+
+        except Exception as e:
+            logging.error("ERROR: Rotation failed for " + subject)
+            logging.error("Reason: " + str(e))
+            rot_tal_x = np.nan
+            rot_tal_y = np.nan
+            rot_tal_z = np.nan
+            metrics_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # store data
+        metricsDict[subject].update(
+            {
+                "wm_snr_orig": wm_snr_orig,
+                "gm_snr_orig": gm_snr_orig,
+                "wm_snr_norm": wm_snr_norm,
+                "gm_snr_norm": gm_snr_norm,
+                "cc_size": cc_size,
+                "holes_lh": holes_lh,
+                "holes_rh": holes_rh,
+                "defects_lh": defects_lh,
+                "defects_rh": defects_rh,
+                "topo_lh": topo_lh,
+                "topo_rh": topo_rh,
+                "con_snr_lh": con_snr_lh,
+                "con_snr_rh": con_snr_rh,
+                "rot_tal_x": rot_tal_x,
+                "rot_tal_y": rot_tal_y,
+                "rot_tal_z": rot_tal_z,
+            }
+        )
+
+        # store data
+        statusDict[subject].update({"metrics": metrics_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: shape analysis
+
+        if argsDict["shape"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Running brainPrint analysis ...")
+                print("")
+
+                from pathlib import Path
+
+                # compute brainprint (will also compute shapeDNA)
+                import brainprint
+
+                # check / create subject-specific brainprint_outdir
+                brainprint_outdir = Path(os.path.join(argsDict["output_dir"], 'brainprint', subject))
+
+                # run brainPrint
+                evMat, evecMat, dstMat = brainprint.brainprint.run_brainprint(subjects_dir=argsDict["subjects_dir"], subject_id=subject, destination=brainprint_outdir, keep_eigenvectors=SHAPE_EVEC, skip_cortex=SHAPE_SKIPCORTEX, num=SHAPE_NUM, norm=SHAPE_NORM, reweight=SHAPE_REWEIGHT, asymmetry=SHAPE_ASYMMETRY)
+
+                # get a subset of the brainprint results
+                distDict = {subject: dstMat}
+
+                # return
+                shape_ok = True
+
+            #
+            except Exception as e:
+                distDict = {subject: []}
+                logging.error("ERROR: the shape module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                shape_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            metricsDict[subject].update(distDict[subject])
+
+            # store data
+            statusDict[subject].update({"shape": shape_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: screenshots
+
+        if argsDict["screenshots"] is True or argsDict["screenshots_html"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Creating screenshots ...")
+                print("")
+
+                # check / create subject-specific screenshots_outdir
+                screenshots_outdir = os.path.join(argsDict["output_dir"], "screenshots", subject)
+                if not os.path.isdir(screenshots_outdir):
+                    os.makedirs(screenshots_outdir)
+                outfile = os.path.join(screenshots_outdir, subject + ".png")
+
+                # re-initialize
+                screenshots_base_subj = list()
+                screenshots_overlay_subj = list()
+                screenshots_surf_subj = list()
+
+                # check screenshots_base
+                if argsDict["screenshots_base"][0] == "default":
+                    screenshots_base_subj = argsDict["screenshots_base"]
+                    logging.info("Using default for screenshot base image")
+                elif os.path.isfile(argsDict["screenshots_base"][0]):
+                    screenshots_base_subj = argsDict["screenshots_base"]
+                    logging.info("Using " + screenshots_base_subj[0] + " as screenshot base image")
+                elif os.path.isfile(
+                    os.path.join(argsDict["subjects_dir"], subject, "mri", argsDict["screenshots_base"][0])
+                ):
+                    screenshots_base_subj = [
+                        os.path.join(argsDict["subjects_dir"], subject, "mri", argsDict["screenshots_base"][0])
+                    ]
+                    logging.info("Using " + screenshots_base_subj[0] + " as screenshot base image")
+                else:
+                    raise FileNotFoundError("ERROR: cannot find the screenshots base file " + argsDict["screenshots_base"][0])
+
+                # check screenshots_overlay
+                if argsDict["screenshots_overlay"] is not None:
+                    if argsDict["screenshots_overlay"][0] == "default":
+                        screenshots_overlay_subj = argsDict["screenshots_overlay"]
+                        logging.info("Using default for screenshot overlay image")
+                    elif os.path.isfile(argsDict["screenshots_overlay"][0]):
+                        screenshots_overlay_subj = argsDict["screenshots_overlay"]
+                        logging.info("Using " + screenshots_overlay_subj[0] + " as screenshot overlay image")
+                    elif os.path.isfile(
+                        os.path.join(argsDict["subjects_dir"], subject, "mri", argsDict["screenshots_overlay"][0])
+                    ):
+                        screenshots_overlay_subj = [
+                            os.path.join(argsDict["subjects_dir"], subject, "mri", argsDict["screenshots_overlay"][0])
+                        ]
+                        logging.info("Using " + screenshots_overlay_subj[0] + " as screenshot overlay image")
+                    else:
+                        raise FileNotFoundError("ERROR: cannot find the screenshots overlay file " + argsDict["screenshots_overlay"][0])
+                else:
+                    screenshots_overlay_subj = argsDict["screenshots_overlay"]
+
+                # check screenshots_surf
+                if argsDict["screenshots_surf"] is not None:
+                    for screenshots_surf_i in argsDict["screenshots_surf"]:
+                        if screenshots_surf_i == "default":
+                            logging.info("Using default for screenshot surface")
+                        elif os.path.isfile(screenshots_surf_i):
+                            logging.info("Using " + screenshots_surf_i + " as screenshot surface")
+                        elif os.path.isfile(
+                            os.path.join(argsDict["subjects_dir"], subject, "surf", screenshots_surf_i)
+                        ):
+                            screenshots_surf_i = os.path.join(
+                                argsDict["subjects_dir"], subject, "surf", screenshots_surf_i
+                            )
+                            logging.info("Using " + screenshots_surf_i + " as screenshot surface")
+                        else:
+                            raise FileNotFoundError("ERROR: cannot find the screenshots surface file " + screenshots_surf_i)
+                        screenshots_surf_subj.append(screenshots_surf_i)
+                else:
+                    screenshots_surf_subj = None
+
+                # process
+                createScreenshots(SUBJECT=subject, SUBJECTS_DIR=argsDict["subjects_dir"], OUTFILE=outfile, INTERACTIVE=False, BASE=screenshots_base_subj, OVERLAY=screenshots_overlay_subj, SURF=screenshots_surf_subj, VIEWS=argsDict["screenshots_views"], LAYOUT=argsDict["screenshots_layout"], ORIENTATION=argsDict["screenshots_orientation"])
+
+                # return
+                screenshots_ok = True
+
+            #
+            except Exception as e:
+                logging.error("ERROR: screenshots module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                screenshots_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            if screenshots_ok:
+                imagesScreenshotsDict[subject] = outfile
+            else:
+                imagesScreenshotsDict[subject] = []
+
+            # store data
+            statusDict[subject].update({"screenshots": screenshots_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: surface plots
+
+        if argsDict["surfaces"] is True or argsDict["surfaces_html"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Creating surface plots ...")
+                print("")
+
+                # check / create subject-specific surfaces_outdir
+                surfaces_outdir = os.path.join(argsDict["output_dir"], "surfaces", subject)
+                if not os.path.isdir(surfaces_outdir):
+                    os.makedirs(surfaces_outdir)
+
+                # process
+                createSurfacePlots(
+                    SUBJECT=subject,
+                    SUBJECTS_DIR=argsDict["subjects_dir"],
+                    SURFACES_OUTDIR=surfaces_outdir,
+                    VIEWS=argsDict["surfaces_views"],
+                    FASTSURFER=argsDict["fastsurfer"]
+                )
+                # return
+                surfaces_ok = True
+
+            #
+            except Exception as e:
+                logging.error("ERROR: surfaces module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                surfaces_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            if surfaces_ok:
+                imagesSurfacesDict[subject] = surfaces_outdir
+            else:
+                imagesSurfacesDict[subject] = []
+
+            # store data
+            statusDict[subject].update({"surfaces": surfaces_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: skullstrip
+
+        if argsDict["skullstrip"] is True or argsDict["skullstrip_html"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Creating skullstrip evaluation  ...")
+                print("")
+
+                # check / create subject-specific skullstrip_outdir
+                skullstrip_outdir = os.path.join(argsDict["output_dir"], "skullstrip", subject)
+                if not os.path.isdir(skullstrip_outdir):
+                    os.makedirs(skullstrip_outdir)
+                outfile = os.path.join(skullstrip_outdir, subject + ".png")
+
+                # re-initialize
+                skullstrip_base_subj = list()
+                skullstrip_overlay_subj = list()
+
+                # check skullstrip_base
+                if os.path.isfile(os.path.join(argsDict["subjects_dir"], subject, "mri", "orig.mgz")):
+                    skullstrip_base_subj = [os.path.join(argsDict["subjects_dir"], subject, "mri", "orig.mgz")]
+                    logging.info("Using " + "orig.mgz" + " as skullstrip base image")
+                else:
+                    raise FileNotFoundError("ERROR: cannot find the skullstrip base file " + "orig.mgz")
+
+                # check skullstrip_overlay
+                if os.path.isfile(os.path.join(argsDict["subjects_dir"], subject, "mri", "brainmask.mgz")):
+                    skullstrip_overlay_subj = [os.path.join(argsDict["subjects_dir"], subject, "mri", "brainmask.mgz")]
+                    logging.info("Using " + "brainmask.mgz" + " as skullstrip overlay image")
+                else:
+                    raise FileNotFoundError("ERROR: cannot find the skullstrip overlay file " + "brainmask.mgz")
+
+                # process
+                createScreenshots(
+                    SUBJECT=subject,
+                    SUBJECTS_DIR=argsDict["subjects_dir"],
+                    OUTFILE=outfile,
+                    INTERACTIVE=False,
+                    BASE=skullstrip_base_subj,
+                    OVERLAY=skullstrip_overlay_subj,
+                    SURF=None,
+                    VIEWS=argsDict["screenshots_views"],
+                    LAYOUT=argsDict["screenshots_layout"],
+                    BINARIZE=True,
+                    ORIENTATION=argsDict["screenshots_orientation"],
+                )
+
+                # return
+                skullstrip_ok = True
+
+            #
+            except Exception as e:
+                logging.error("ERROR: skullstrip module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                skullstrip_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            if skullstrip_ok:
+                imagesSkullstripDict[subject] = outfile
+            else:
+                imagesSkullstripDict[subject] = []
+
+            # store data
+            statusDict[subject].update({"skullstrip": skullstrip_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: fornix
+
+        if argsDict["fornix"] is True or argsDict["fornix_html"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Checking fornix segmentation ...")
+                print("")
+
+                # check / create subject-specific fornix_outdir
+                fornix_outdir = os.path.join(argsDict["output_dir"], "fornix", subject)
+                if not os.path.isdir(fornix_outdir):
+                    os.makedirs(fornix_outdir)
+                fornix_screenshot_outfile = os.path.join(fornix_outdir, "cc.png")
+
+                # process
+                fornixShapeOutput = evaluateFornixSegmentation(
+                    SUBJECT=subject,
+                    SUBJECTS_DIR=argsDict["subjects_dir"],
+                    OUTPUT_DIR=fornix_outdir,
+                    CREATE_SCREENSHOT=FORNIX_SCREENSHOT,
+                    SCREENSHOTS_OUTFILE=fornix_screenshot_outfile,
+                    RUN_SHAPEDNA=FORNIX_SHAPE,
+                    N_EIGEN=FORNIX_N_EIGEN,
+                )
+
+                # create a dictionary from fornix shape output
+                fornixShapeDict = {
+                    subject: dict(zip(map("fornixShapeEV{:0>3}".format, range(FORNIX_N_EIGEN)), fornixShapeOutput))
+                }
+
+                # return
+                fornix_ok = True
+
+            #
+            except Exception as e:
+                fornixShapeDict = {
+                    subject: dict(
+                        zip(map("fornixShapeEV{:0>3}".format, range(FORNIX_N_EIGEN)), np.full(FORNIX_N_EIGEN, np.nan))
+                    )
+                }
+                logging.error("ERROR: fornix module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                fornix_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            if FORNIX_SHAPE:
+                metricsDict[subject].update(fornixShapeDict[subject])
+
+            # store data
+            if FORNIX_SCREENSHOT and fornix_ok:
+                imagesFornixDict[subject] = fornix_screenshot_outfile
+            else:
+                imagesFornixDict[subject] = []
+
+            # store data
+            statusDict[subject].update({"fornix": fornix_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: hypothalamus
+
+        if argsDict["hypothalamus"] is True or argsDict["hypothalamus_html"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Checking hypothalamus segmentation ...")
+                print("")
+
+                # check / create subject-specific hypothalamus_outdir
+                hypothalamus_outdir = os.path.join(argsDict["output_dir"], "hypothalamus", subject)
+                if not os.path.isdir(hypothalamus_outdir):
+                    os.makedirs(hypothalamus_outdir)
+                hypothalamus_screenshot_outfile = os.path.join(hypothalamus_outdir, "hypothalamus.png")
+
+                # process
+                evaluateHypothalamicSegmentation(
+                    SUBJECT=subject,
+                    SUBJECTS_DIR=argsDict["subjects_dir"],
+                    OUTPUT_DIR=hypothalamus_outdir,
+                    CREATE_SCREENSHOT=HYPOTHALAMUS_SCREENSHOT,
+                    SCREENSHOTS_OUTFILE=hypothalamus_screenshot_outfile,
+                    SCREENSHOTS_ORIENTATION=argsDict["screenshots_orientation"],
+                )
+
+                # return
+                hypothalamus_ok = True
+
+            #
+            except Exception as e:
+                logging.error("ERROR: hypothalamus module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                hypothalamus_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            if HYPOTHALAMUS_SCREENSHOT and hypothalamus_ok:
+                imagesHypothalamusDict[subject] = hypothalamus_screenshot_outfile
+            else:
+                imagesHypothalamusDict[subject] = []
+
+            # store data
+            statusDict[subject].update({"hypothalamus": hypothalamus_ok})
+
+        # ----------------------------------------------------------------------
+        # run optional modules: hippocampus
+
+        if argsDict["hippocampus"] is True or argsDict["hippocampus_html"] is True:
+            #
+            try:
+                # message
+                print("-----------------------------")
+                print("Checking hippocampus segmentation ...")
+                print("")
+
+                # check / create subject-specific hippocampus_outdir
+                hippocampus_outdir = os.path.join(argsDict["output_dir"], "hippocampus", subject)
+                if not os.path.isdir(hippocampus_outdir):
+                    os.makedirs(hippocampus_outdir)
+                hippocampus_screenshot_outfile_left = os.path.join(hippocampus_outdir, "hippocampus-left.png")
+                hippocampus_screenshot_outfile_right = os.path.join(hippocampus_outdir, "hippocampus-right.png")
+
+                # process left
+                evaluateHippocampalSegmentation(
+                    SUBJECT=subject,
+                    SUBJECTS_DIR=argsDict["subjects_dir"],
+                    OUTPUT_DIR=hippocampus_outdir,
+                    CREATE_SCREENSHOT=HIPPOCAMPUS_SCREENSHOT,
+                    SCREENSHOTS_OUTFILE=hippocampus_screenshot_outfile_left,
+                    SCREENSHOTS_ORIENTATION=argsDict["screenshots_orientation"],
+                    HEMI="lh",
+                    LABEL=argsDict["hippocampus_label"],
+                )
+                evaluateHippocampalSegmentation(
+                    SUBJECT=subject,
+                    SUBJECTS_DIR=argsDict["subjects_dir"],
+                    OUTPUT_DIR=hippocampus_outdir,
+                    CREATE_SCREENSHOT=HIPPOCAMPUS_SCREENSHOT,
+                    SCREENSHOTS_OUTFILE=hippocampus_screenshot_outfile_right,
+                    SCREENSHOTS_ORIENTATION=argsDict["screenshots_orientation"],
+                    HEMI="rh",
+                    LABEL=argsDict["hippocampus_label"],
+                )
+
+                # return
+                hippocampus_ok = True
+
+            #
+            except Exception as e:
+                logging.error("ERROR: hippocampus module failed for subject " + subject)
+                logging.error("Reason: " + str(e))
+                hippocampus_ok = False
+                if argsDict["exit_on_error"] is True:
+                    raise
+
+            # store data
+            if HIPPOCAMPUS_SCREENSHOT and hippocampus_ok:
+                imagesHippocampusLeftDict[subject] = hippocampus_screenshot_outfile_left
+                imagesHippocampusRightDict[subject] = hippocampus_screenshot_outfile_right
+            else:
+                imagesHippocampusLeftDict[subject] = []
+                imagesHippocampusRightDict[subject] = []
+
+            # store data
+            statusDict[subject].update({"hippocampus": hippocampus_ok})
+
+        # --------------------------------------------------------------------------
+        # message
+        logging.info("Finished subject " +  subject + " at " + time.strftime("%Y-%m-%d %H:%M %Z", time.localtime(time.time())))
+
+    # --------------------------------------------------------------------------
+    # run optional modules: outlier detection
+
+    if argsDict["outlier"] is True:
+        #
+        try:
+            # message
+            print("---------------------------------------")
+            print("Running outlier detection module ...")
+            print("")
+
+            # determine outlier-table and get data
+            if argsDict["outlier_table"] is None:
+                outlierDict = outlierTable()
+            else:
+                outlierDict = dict()
+                with open(argsDict["outlier_table"], newline="") as csvfile:
+                    outlierCsv = csv.DictReader(csvfile, delimiter=",")
+                    for row in outlierCsv:
+                        outlierDict.update({row["label"]: {"lower": float(row["lower"]), "upper": float(row["upper"])}})
+
+            # process
+            outlier_outdir = os.path.join(argsDict["output_dir"], "outliers")
+            n_outlier_sample_nonpar, n_outlier_sample_param, n_outlier_norms = outlierDetection(
+                argsDict["subjects"],
+                argsDict["subjects_dir"],
+                outlier_outdir,
+                outlierDict,
+                min_no_subjects=OUTLIER_N_MIN,
+                hypothalamus=argsDict["hypothalamus"],
+                hippocampus=argsDict["hippocampus"],
+                hippocampus_label=argsDict["hippocampus_label"],
+            )
+
+            # create a dictionary from outlier module output
+            outlierDict = dict()
+            for subject in argsDict["subjects"]:
+                outlierDict.update(
+                    {
+                        subject: {
+                            "n_outlier_sample_nonpar": n_outlier_sample_nonpar[subject],
+                            "n_outlier_sample_param": n_outlier_sample_param[subject],
+                            "n_outlier_norms": n_outlier_norms[subject],
+                        }
+                    }
+                )
+
+            # return
+            outlier_ok = True
+
+        #
+        except Exception as e:
+            # create a dictionary from outlier module output
+            outlierDict = dict()
+            for subject in argsDict["subjects"]:
+                outlierDict.update(
+                    {
+                        subject: {
+                            "n_outlier_sample_nonpar": np.nan,
+                            "n_outlier_sample_param": np.nan,
+                            "n_outlier_norms": np.nan,
+                        }
+                    }
+                )
+
+            logging.error("ERROR: outlier module failed")
+            logging.error("Reason: " + str(e))
+            outlier_ok = False
+            if argsDict["exit_on_error"] is True:
+                raise
+
+        # store data
+        for subject in argsDict["subjects"]:
+            metricsDict[subject].update(outlierDict[subject])
+
+        # message
+        logging.info("Done")
+
+    # --------------------------------------------------------------------------
+    # generate output
+
+    metricsFieldnames = ["subject"]
+
+    # we pre-specify the fieldnames because we want to have this particular order
+    metricsFieldnames.extend(
+        [
+            "wm_snr_orig",
+            "gm_snr_orig",
+            "wm_snr_norm",
+            "gm_snr_norm",
+            "cc_size",
+            "holes_lh",
+            "holes_rh",
+            "defects_lh",
+            "defects_rh",
+            "topo_lh",
+            "topo_rh",
+            "con_snr_lh",
+            "con_snr_rh",
+            "rot_tal_x",
+            "rot_tal_y",
+            "rot_tal_z",
+        ]
+    )
+
+    # collect other keys; need to iterate over subjects, because not all of them
+    # necessarily have the same set of keys
+    if argsDict["shape"] is True:
+        shapeKeys = list()
+        for subject in distDict.keys():
+            if len(distDict[subject]) > 0:
+                shapeKeys = list(np.unique(shapeKeys + list(distDict[subject].keys())))
+        metricsFieldnames.extend(shapeKeys)
+
+    if (argsDict["fornix"] is True or argsDict["fornix_html"] is True) and FORNIX_SHAPE is True:
+        fornixKeys = list()
+        for subject in fornixShapeDict.keys():
+            if len(fornixShapeDict[subject]) > 0:
+                fornixKeys = list(np.unique(fornixKeys + list(fornixShapeDict[subject].keys())))
+        metricsFieldnames.extend(sorted(fornixKeys))
+
+    if argsDict["outlier"] is True:
+        outlierKeys = list()
+        for subject in outlierDict.keys():
+            if len(outlierDict[subject]) > 0:
+                outlierKeys = list(np.unique(outlierKeys + list(outlierDict[subject].keys())))
+        metricsFieldnames.extend(sorted(outlierKeys))
+
+    # determine output file names
+    path_data_file = os.path.join(argsDict["output_dir"], "fsqc-results.csv")
+    path_html_file = os.path.join(argsDict["output_dir"], "fsqc-results.html")
+
+    # write csv
+    with open(path_data_file, "w") as datafile:
+        csvwriter = csv.DictWriter(
+            datafile, fieldnames=metricsFieldnames, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
+        )
+        csvwriter.writeheader()
+        for subject in sorted(list(metricsDict.keys())):
+            csvwriter.writerow(metricsDict[subject])
+
+    # generate html output
+    if (
+        (argsDict["screenshots_html"] is True)
+        or (argsDict["surfaces_html"] is True)
+        or (argsDict["skullstrip_html"] is True)
+        or (argsDict["fornix_html"] is True)
+        or (argsDict["hypothalamus_html"] is True)
+        or (argsDict["hippocampus_html"] is True)
+    ):
+        with open(path_html_file, "w") as htmlfile:
+            print("<html>", file=htmlfile)
+            print("<head>", file=htmlfile)
+            print("<title>fsqc screenshots</title>", file=htmlfile)
+            print("</head>", file=htmlfile)
+            print(
+                "<style> body, h1, h2, h3, h4, h5, h6  { font-family: Arial, Helvetica, sans-serif ; } </style>)",
+                file=htmlfile,
+            )
+            print('<body style="background-color:Black">', file=htmlfile)
+
+            # screenshots
+            if argsDict["screenshots_html"] is True:
+                print('<h1 style="color:white">Screenshots</h1>', file=htmlfile)
+                for subject in sorted(list(imagesScreenshotsDict.keys())):
+                    print('<h2 style="color:white">Subject ' + subject + "</h2>", file=htmlfile)
+                    if imagesScreenshotsDict[subject]:  # should be False for empty string or empty list
+                        if os.path.isfile(
+                            os.path.join(
+                                argsDict["output_dir"],
+                                "screenshots",
+                                subject,
+                                os.path.basename(imagesScreenshotsDict[subject]),
+                            )
+                        ):
+                            print(
+                                '<p><a href="'
+                                + os.path.join("screenshots", subject, os.path.basename(imagesScreenshotsDict[subject]))
+                                + '">'
+                                + '<img src="'
+                                + os.path.join("screenshots", subject, os.path.basename(imagesScreenshotsDict[subject]))
+                                + '" '
+                                + 'alt="Image for subject '
+                                + subject
+                                + '" style="width:75vw;min_width:200px;"></img></a></p>',
+                                file=htmlfile,
+                            )
+
+            # skullstrip
+            if argsDict["skullstrip_html"] is True:
+                print('<h1 style="color:white">Skullstrip</h1>', file=htmlfile)
+                for subject in sorted(list(imagesSkullstripDict.keys())):
+                    print('<h2 style="color:white">Subject ' + subject + "</h2>", file=htmlfile)
+                    if imagesSkullstripDict[subject]:  # should be False for empty string or empty list
+                        if os.path.isfile(
+                            os.path.join(
+                                argsDict["output_dir"],
+                                "skullstrip",
+                                subject,
+                                os.path.basename(imagesSkullstripDict[subject]),
+                            )
+                        ):
+                            print(
+                                '<p><a href="'
+                                + os.path.join("skullstrip", subject, os.path.basename(imagesSkullstripDict[subject]))
+                                + '">'
+                                + '<img src="'
+                                + os.path.join("skullstrip", subject, os.path.basename(imagesSkullstripDict[subject]))
+                                + '" '
+                                + 'alt="Image for subject '
+                                + subject
+                                + '" style="width:75vw;min_width:200px;"></img></a></p>',
+                                file=htmlfile,
+                            )
+
+            # surfaces
+            if argsDict["surfaces_html"] is True:
+                print('<h1 style="color:white">Surfaces</h1>', file=htmlfile)
+                for subject in sorted(list(imagesSurfacesDict.keys())):
+                    print('<h2 style="color:white">Subject ' + subject + "</h2>", file=htmlfile)
+                    if imagesSurfacesDict[subject]:  # should be False for empty string or empty list
+                        # Produce first all plots for pial then for inflated surface.
+                        # Each view contains a left and right hemispheric plot.
+                        _views_per_row = 2
+
+                        from PIL import Image
+
+                        filepath = os.path.join(
+                            argsDict["output_dir"], "surfaces", subject, f'lh.pial.{argsDict["surfaces_views"][0]}.png'
+                        )
+                        img = Image.open(filepath)
+                        width, height = img.size
+                        width *= 2 * _views_per_row + 0.1
+
+                        print("<p>", file=htmlfile)
+                        print(f'<div style="width:{width}; background-color:black; ">', file=htmlfile)
+                        print("<p>", file=htmlfile)
+                        for i, v in enumerate(argsDict["surfaces_views"], start=1):
+                            if os.path.isfile(
+                                os.path.join(argsDict["output_dir"], "surfaces", subject, f"lh.pial.{v}.png")
+                            ):
+                                print(
+                                    '<a href="'
+                                    + os.path.join("surfaces", subject, f"lh.pial.{v}.png")
+                                    + '">'
+                                    + '<img src="'
+                                    + os.path.join("surfaces", subject, f"lh.pial.{v}.png")
+                                    + '" '
+                                    + 'alt="Image for subject '
+                                    + subject
+                                    + '" style=""></img></a>',
+                                    file=htmlfile,
+                                )
+                            if os.path.isfile(
+                                os.path.join(argsDict["output_dir"], "surfaces", subject, f"rh.pial.{v}.png")
+                            ):
+                                print(
+                                    '<a href="'
+                                    + os.path.join("surfaces", subject, f"rh.pial.{v}.png")
+                                    + '">'
+                                    + '<img src="'
+                                    + os.path.join("surfaces", subject, f"rh.pial.{v}.png")
+                                    + '" '
+                                    + 'alt="Image for subject '
+                                    + subject
+                                    + '" style=""></img></a>',
+                                    file=htmlfile,
+                                )
+                            if i % _views_per_row == 0:
+                                print("</p> <p>", file=htmlfile)
+                        print("</p> <p>", file=htmlfile)
+                        for i, v in enumerate(argsDict["surfaces_views"], start=1):
+                            if os.path.isfile(
+                                os.path.join(argsDict["output_dir"], "surfaces", subject, f"lh.inflated.{v}.png")
+                            ):
+                                print(
+                                    '<a href="'
+                                    + os.path.join("surfaces", subject, f"lh.inflated.{v}.png")
+                                    + '">'
+                                    + '<img src="'
+                                    + os.path.join("surfaces", subject, f"lh.inflated.{v}.png")
+                                    + '" '
+                                    + 'alt="Image for subject '
+                                    + subject
+                                    + '" style=""></img></a>',
+                                    file=htmlfile,
+                                )
+                            if os.path.isfile(
+                                os.path.join(argsDict["output_dir"], "surfaces", subject, f"rh.inflated.{v}.png")
+                            ):
+                                print(
+                                    '<a href="'
+                                    + os.path.join("surfaces", subject, f"rh.inflated.{v}.png")
+                                    + '">'
+                                    + '<img src="'
+                                    + os.path.join("surfaces", subject, f"rh.inflated.{v}.png")
+                                    + '" '
+                                    + 'alt="Image for subject '
+                                    + subject
+                                    + '" style=""></img></a>',
+                                    file=htmlfile,
+                                )
+                            if i % _views_per_row == 0:
+                                print("</p> <p>", file=htmlfile)
+                        print("</p>", file=htmlfile)
+                        print("</div>", file=htmlfile)
+                        print("</p>", file=htmlfile)
+
+            # fornix
+            if argsDict["fornix_html"] is True:
+                print('<h1 style="color:white">Fornix</h1>', file=htmlfile)
+                for subject in sorted(list(imagesFornixDict.keys())):
+                    print('<h2 style="color:white">Subject ' + subject + "</h2>", file=htmlfile)
+                    if imagesFornixDict[subject]:  # should be False for empty string or empty list
+                        if os.path.isfile(
+                            os.path.join(
+                                argsDict["output_dir"], "fornix", subject, os.path.basename(imagesFornixDict[subject])
+                            )
+                        ):
+                            print(
+                                '<p><a href="'
+                                + os.path.join("fornix", subject, os.path.basename(imagesFornixDict[subject]))
+                                + '">'
+                                + '<img src="'
+                                + os.path.join("fornix", subject, os.path.basename(imagesFornixDict[subject]))
+                                + '" '
+                                + 'alt="Image for subject '
+                                + subject
+                                + '" style="width:75vw;min_width:200px;"></img></a></p>',
+                                file=htmlfile,
+                            )
+
+            # hypothalamus
+            if argsDict["hypothalamus_html"] is True:
+                print('<h1 style="color:white">Hypothalamus</h1>', file=htmlfile)
+                for subject in sorted(list(imagesHypothalamusDict.keys())):
+                    print('<h2 style="color:white">Subject ' + subject + "</h2>", file=htmlfile)
+                    if imagesHypothalamusDict[subject]:  # should be False for empty string or empty list
+                        if os.path.isfile(
+                            os.path.join(
+                                argsDict["output_dir"],
+                                "hypothalamus",
+                                subject,
+                                os.path.basename(imagesHypothalamusDict[subject]),
+                            )
+                        ):
+                            print(
+                                '<p><a href="'
+                                + os.path.join(
+                                    "hypothalamus", subject, os.path.basename(imagesHypothalamusDict[subject])
+                                )
+                                + '">'
+                                + '<img src="'
+                                + os.path.join(
+                                    "hypothalamus", subject, os.path.basename(imagesHypothalamusDict[subject])
+                                )
+                                + '" '
+                                + 'alt="Image for subject '
+                                + subject
+                                + '" style="width:75vw;min_width:200px;"></img></a></p>',
+                                file=htmlfile,
+                            )
+
+            # hippocampus
+            if argsDict["hippocampus_html"] is True:
+                print('<h1 style="color:white">hippocampus</h1>', file=htmlfile)
+                for subject in sorted(list(imagesHippocampusLeftDict.keys())):
+                    print('<h2 style="color:white">Subject ' + subject + "</h2>", file=htmlfile)
+                    if imagesHippocampusLeftDict[subject]:  # should be False for empty string or empty list
+                        if os.path.isfile(
+                            os.path.join(
+                                argsDict["output_dir"],
+                                "hippocampus",
+                                subject,
+                                os.path.basename(imagesHippocampusLeftDict[subject]),
+                            )
+                        ):
+                            print(
+                                '<p><a href="'
+                                + os.path.join(
+                                    "hippocampus", subject, os.path.basename(imagesHippocampusLeftDict[subject])
+                                )
+                                + '">'
+                                + '<img src="'
+                                + os.path.join(
+                                    "hippocampus", subject, os.path.basename(imagesHippocampusLeftDict[subject])
+                                )
+                                + '" '
+                                + 'alt="Image for subject '
+                                + subject
+                                + '" style="width:75vw;min_width:200px;"></img></a></p>',
+                                file=htmlfile,
+                            )
+                    if imagesHippocampusRightDict[subject]:  # should be False for empty string or empty list
+                        if os.path.isfile(
+                            os.path.join(
+                                argsDict["output_dir"],
+                                "hippocampus",
+                                subject,
+                                os.path.basename(imagesHippocampusRightDict[subject]),
+                            )
+                        ):
+                            print(
+                                '<p><a href="'
+                                + os.path.join(
+                                    "hippocampus", subject, os.path.basename(imagesHippocampusRightDict[subject])
+                                )
+                                + '">'
+                                + '<img src="'
+                                + os.path.join(
+                                    "hippocampus", subject, os.path.basename(imagesHippocampusRightDict[subject])
+                                )
+                                + '" '
+                                + 'alt="Image for subject '
+                                + subject
+                                + '" style="width:75vw;min_width:200px;"></img></a></p>',
+                                file=htmlfile,
+                            )
+
+            #
+            print("</body>", file=htmlfile)
+            print("</html>", file=htmlfile)
+
+
+# ------------------------------------------------------------------------------
+# _start_logging
+
+def _start_logging(argsDict):
+    """
+    start logging
+
+    """
+
+    # imports
+    import os
+    import sys
+    import uuid
+    import time
+    import logging
+    import tempfile
+    import traceback
+
+    # setup function to log uncaught exceptions
+    def foo(exctype, value, tb):
+        # log
+        logging.error('Error Information:')
+        logging.error('Type: %s', exctype)
+        logging.error('Value: %s', value)
+        for i in traceback.format_list(traceback.extract_tb(tb)):
+            logging.error('Traceback: %s', i)
+        # message
+        logging.error('Status: program exited with errors')
+    sys.excepthook = foo
+
+    # set up logging
+    logfile_format = "[%(levelname)s: %(filename)s: %(lineno)4d]: %(message)s"
+    logfile_handlers = [logging.StreamHandler(sys.stdout)]
+    logging.basicConfig(level=logging.INFO, format=logfile_format, handlers=logfile_handlers)
+
+    # check if output directory exists or can be created
+    if os.path.isdir(argsDict["output_dir"]):
+        logging.info("Found output directory " + argsDict["output_dir"])
+    else:
+        try:
+            os.mkdir(argsDict["output_dir"])
+        except Exception as e:
+            logging.error("ERROR: cannot create output directory " + argsDict["output_dir"])
+            logging.error("Reason: " + str(e))
+            raise
+
+    # check if logfile can be written in output directory
+    try:
+        testfile = tempfile.TemporaryFile(dir=argsDict["output_dir"])
+        testfile.close()
+    except Exception as e:
+        logging.error("ERROR: " + argsDict["output_dir"] + " not writeable")
+        logging.error("Reason: " + str(e))
+        raise
+
+    #
+    logfile =  os.path.join(argsDict["output_dir"], 'logfile.txt')
+    logging.getLogger().addHandler(logging.FileHandler(filename=logfile, mode="w"))
+
+    # intial messages
+    logging.info("Starting logging for fsqctools ...")
+    logging.info("Logfile: %s", logfile)
+    logging.info("Version: %s", get_version())
+    logging.info("Date: %s", time.strftime('%d/%m/%Y %H:%M:%S'))
+
+    # log args
+    logging.info("Command: " + " ".join(sys.argv))
+
+    # update args
+    argsDict["logfile"] = logfile
+
+    # return
+    return argsDict
+
+
+# ------------------------------------------------------------------------------
+# run fsqc
+
+
+def run_fsqc(
+    subjects_dir,
+    output_dir,
+    argsDict=None,
+    subjects=None,
+    subjects_file=None,
+    shape=False,
+    screenshots=False,
+    screenshots_html=False,
+    screenshots_base="default",
+    screenshots_overlay="default",
+    screenshots_surf="default",
+    screenshots_views="default",
+    screenshots_layout=None,
+    screenshots_orientation=["radiological"],
+    surfaces=False,
+    surfaces_html=False,
+    surfaces_views=["left", "right", "superior", "inferior"],
+    skullstrip=False,
+    skullstrip_html=False,
+    fornix=False,
+    fornix_html=False,
+    hypothalamus=False,
+    hypothalamus_html=False,
+    hippocampus=False,
+    hippocampus_html=False,
+    hippocampus_label=None,
+    outlier=False,
+    outlier_table=None,
+    fastsurfer=False,
+    exit_on_error=False,
+    logfile=None,
+):
+    """Run the fsqc submodules.
+
+    This is the main function to run the fsqc submodules.
+
+    Parameters
+    ----------
+    subjects_dir : str
+        Subjects directory.
+    output_dir : str
+        Output directory.
+    argsDict : dict, default: None
+        Dictionary of input arguments.
+    subjects : list of str, default: None
+        List of subjects to process. If None, all valid cases in the input
+        directory will be processed. Cannot be used with `subjects_file`.
+    subjects_file : str, default: None
+        A text file that contains a list of subjects to be processed. Cannot
+        be used with `subjects`.
+    shape : bool, default: False
+        Conduct shape analysis using brainprint.
+    screenshots : bool, default: False
+        Create screenshots of MR image with optional overlays.
+    screenshots_html : bool, default: False
+        Create screenshots and html summary page.
+    screenshots_base : str, default: "default"
+        Filename for base image for screenshots.
+    screenshots_overlay : str, default: "default"
+        Filename for overlay image for screenshots.
+    screenshots_surf : list of str, default: "default"
+        List of filenames of surface files to include in screenshots.
+    screenshots_views : list of str, default: "default"
+        List of parameters to set the views of the screenshots.
+        Example: ['x=0', 'x=-10', 'x=10', 'y=20', 'z=0']
+    screenshots_layout : list of int, default: None
+        Layout describing rows and columns of the screenshots.
+        Example: [1, 4] (one row, four columns).
+    screenshots_orientation : list of str, default: ["radiological"]
+        Orientation of screenshots. Either "radiological" or "neurological".
+    surfaces : bool, default: False
+        Create screenshots of pial and inflated surfaces.
+    surfaces_html : bool, default: False
+        Create screenshots of pial and inflated surfaces and html summary page.
+    surfaces_views : list of str, default: ["left", "right", "superior", "inferior"]
+        List of parameters to set the views of the surface plots.
+    skullstrip : bool, default: False
+        Create screeenshot of MR image and skullstrip overlay.
+    skullstrip_html : bool, default: False
+        Create screeenshot of MR image and skullstrip overlay, and create html
+        summary page.
+    fornix : bool, default: False
+        Create screenshot of MR imge and corpus callosum overlay to identify
+        fornix oversegmentation.
+    fornix_html : bool, default: False
+        Create screenshot of MR imge and corpus callosum overlay with html
+        summary page.
+    hypothalamus : bool, default: False
+        Create screenhots of hypothalamic segmentation. Requires running
+        FreeSurfer's hypothalamus add-on module.
+    hypothalamus_html : bool, default: False
+        Create screenhots of hypothalamic segmentation with html summary page.
+        Requires running FreeSurfer's hypothalamus add-on module.
+    hippocampus : bool, default: False
+        Create screenhots of hippocampal segmentation. Requires running
+        FreeSurfer's hippocampus add-on module.
+    hippocampus_html : bool, default: False
+        Create screenhots of hippocampal segmentation with html summary page.
+        Requires running FreeSurfer's hippocampus add-on module.
+    hippocampus_label : str, default: None
+        Label used for the hippocampal segmentations. Example: "T1.v21". The
+        full filename would be [lr]h.hippoAmygLabels-<LABEL>.FSvoxelSpace.mgz.
+    outlier : bool, default: False
+        Conduct outlier analysis.
+    outlier_table : str, default: None
+        Specify custom norms table for outlier analysis.
+    fastsurfer : bool, default: False
+        Use FastSurfer instead of FreeSurfer input.
+    exit_on_error : bool, default: False
+        Exit on error. If False, a warning is thrown and the analysis
+        continues.
+    logfile : str, default: None
+        Specify a custom location for the logfile. Default location is the
+        output directory.
+
+    Returns
+    -------
+    dict
+        A dictionary of input arguments and processing directives
+
+    """
+
+    # ------------------------------------------------------------------------------
+    #
+
+    #
+    import sys
+
+    # create argsDict
+    if argsDict is None and (subjects_dir is None or output_dir is None):
+        raise ValueError("ERROR: nothing to do. Need to specify either the argsDict argument or the subjects_dir / output_dir arguments.")
+
+    elif argsDict is None and subjects_dir is not None and output_dir is not None:
+        argsDict = dict()
+        argsDict["subjects_dir"] = subjects_dir
+        argsDict["output_dir"] = output_dir
+        argsDict["subjects"] = subjects
+        argsDict["subjects_file"] = subjects_file
+        argsDict["shape"] = shape
+        argsDict["screenshots"] = screenshots
+        argsDict["screenshots_html"] = screenshots_html
+        argsDict["screenshots_base"] = screenshots_base
+        argsDict["screenshots_overlay"] = screenshots_overlay
+        argsDict["screenshots_surf"] = screenshots_surf
+        argsDict["screenshots_views"] = screenshots_views
+        argsDict["screenshots_layout"] = screenshots_layout
+        argsDict["screenshots_orientation"] = screenshots_orientation
+        argsDict["surfaces"] = surfaces
+        argsDict["surfaces_html"] = surfaces_html
+        argsDict["surfaces_views"] = surfaces_views
+        argsDict["skullstrip"] = skullstrip
+        argsDict["skullstrip_html"] = skullstrip_html
+        argsDict["fornix"] = fornix
+        argsDict["fornix_html"] = fornix_html
+        argsDict["hypothalamus"] = hypothalamus
+        argsDict["hypothalamus_html"] = hypothalamus_html
+        argsDict["hippocampus"] = hippocampus
+        argsDict["hippocampus_html"] = hippocampus_html
+        argsDict["hippocampus_label"] = hippocampus_label
+        argsDict["outlier"] = outlier
+        argsDict["outlier_table"] = outlier_table
+        argsDict["fastsurfer"] = fastsurfer
+        argsDict["exit_on_error"] = exit_on_error
+        argsDict["logfile"] = logfile
+
+    elif (argsDict is not None) and (subjects_dir is not None or output_dir is not None):
+        raise ValueError("ERROR: cannot specify the argsDict and the subjects_dir / output_dir arguments at the same time.")
+
+    # start logging
+    argsDict = _start_logging(argsDict)
+
+    # check arguments
+    argsDict = _check_arguments(argsDict)
+
+    # check packages
+    _check_packages()
+
+    # run fsqc
+    _do_fsqc(argsDict)
+
+    # return
+    return argsDict
+
+
diff --git a/fsqc/fsqcUtils.py b/fsqc/fsqcUtils.py
new file mode 100644
index 0000000..74112d3
--- /dev/null
+++ b/fsqc/fsqcUtils.py
@@ -0,0 +1,1861 @@
+"""
+This module provides various import/export functions as well as the
+'levelsetsTria' function
+
+"""
+
+# ------------------------------------------------------------------------------
+
+
+def importMGH(filename):
+    """
+    A function to read Freesurfer MGH files.
+
+    Required arguments:
+        - filename
+
+    Returns:
+        - vol
+
+    Requires valid mgh file. If not found, NaNs will be returned.
+
+    """
+
+    import os
+    import logging
+    import numpy
+    import struct
+    import warnings
+
+    logging.captureWarnings(True)
+
+    if not os.path.exists(filename):
+        warnings.warn("WARNING: could not find " + filename + ", returning NaNs")
+        return numpy.nan
+
+    fp = open(filename, "rb")
+    intsize = struct.calcsize(">i")
+    shortsize = struct.calcsize(">h")
+    floatsize = struct.calcsize(">f")
+    charsize = struct.calcsize(">b")
+
+    v = struct.unpack(">i", fp.read(intsize))[0]
+    ndim1 = struct.unpack(">i", fp.read(intsize))[0]
+    ndim2 = struct.unpack(">i", fp.read(intsize))[0]
+    ndim3 = struct.unpack(">i", fp.read(intsize))[0]
+    nframes = struct.unpack(">i", fp.read(intsize))[0]
+    vtype = struct.unpack(">i", fp.read(intsize))[0]
+    dof = struct.unpack(">i", fp.read(intsize))[0]
+
+    UNUSED_SPACE_SIZE = 256
+    USED_SPACE_SIZE = (3 * 4) + (4 * 3 * 4)  # space for ras transform
+    unused_space_size = UNUSED_SPACE_SIZE - 2
+
+    ras_good_flag = struct.unpack(">h", fp.read(shortsize))[0]
+    if ras_good_flag:
+        # We read these in but don't process them
+        # as we just want to move to the volume data
+        delta = struct.unpack(">fff", fp.read(floatsize * 3))
+        Mdc = struct.unpack(">fffffffff", fp.read(floatsize * 9))
+        Pxyz_c = struct.unpack(">fff", fp.read(floatsize * 3))
+
+    unused_space_size = unused_space_size - USED_SPACE_SIZE
+
+    for i in range(unused_space_size):
+        struct.unpack(">b", fp.read(charsize))[0]
+
+    nv = ndim1 * ndim2 * ndim3 * nframes
+    vol = numpy.frombuffer(fp.read(floatsize * nv), dtype=numpy.float32).byteswap()
+
+    nvert = max([ndim1, ndim2, ndim3])
+    vol = numpy.reshape(vol, (ndim1, ndim2, ndim3, nframes), order="F")
+    vol = numpy.squeeze(vol)
+    fp.close()
+
+    return vol
+
+
+# ------------------------------------------------------------------------------
+
+
+def binarizeImage(img_file, out_file, match=None):
+    import nibabel as nb
+    import numpy as np
+
+    # get image
+    img = nb.load(img_file)
+    img_data = img.get_fdata()
+
+    # binarize
+    if match is None:
+        img_data_bin = img_data != 0.0
+    else:
+        img_data_bin = np.isin(img_data, match)
+
+    # write output
+    img_bin = nb.nifti1.Nifti1Image(img_data_bin.astype(int), img.affine, dtype="uint8")
+    nb.save(img_bin, out_file)
+
+
+# ------------------------------------------------------------------------------
+
+
+def applyTransform(img_file, out_file, mat_file, interp):
+    import os
+
+    import nibabel as nb
+    import numpy as np
+    from scipy import ndimage
+
+    # get image
+    img = nb.load(img_file)
+    img_data = img.get_fdata()
+
+    #
+    _, mat_file_ext = os.path.splitext(mat_file)
+
+    # get matrix
+    if mat_file_ext == ".xfm":
+        raise Exception("ERROR: xfm matrices not (yet) supported. Please convert to lta format")
+    elif mat_file_ext == ".lta":
+        # get lta matrix
+        lta = readLTA(mat_file)
+        # get vox2vox transform
+        if lta["type"] == 1:
+            # compute vox2vox from ras2ras as vox2ras2ras2vox transform:
+            # vox2ras from input image (source)
+            # ras2ras from make_upright.lta
+            # ras2vox from upright image (target)
+            # m = np.matmul(np.linalg.inv(upr.affine), np.matmul(lta['lta'], img.affine))
+            raise Exception("ERROR: lta type 1 (ras2ras) not supported yet")
+        elif lta["type"] == 0:
+            # vox2vox transform
+            m = lta["lta"]
+    else:
+        raise Exception("ERROR: matrices must be either xfm or lta format")
+
+    # apply transform
+    if interp == "nearest":
+        img_data_interp = ndimage.affine_transform(img_data, np.linalg.inv(m), order=0)
+    elif interp == "cubic":
+        img_data_interp = ndimage.affine_transform(img_data, np.linalg.inv(m), order=3)
+    else:
+        raise Exception("ERROR: interpolation must be either nearest or cubic")
+
+    # write image
+    img_interp = nb.nifti1.Nifti1Image(img_data_interp, img.affine)
+    nb.save(img_interp, out_file)
+
+
+# ------------------------------------------------------------------------------
+
+
+def readLTA(file):
+    import re
+
+    import numpy as np
+
+    with open(file, "r") as f:
+        lta = f.readlines()
+    d = dict()
+    i = 0
+    while i < len(lta):
+        if re.match("type", lta[i]) is not None:
+            d["type"] = int(re.sub("=", "", re.sub("[a-z]+", "", re.sub("#.*", "", lta[i]))).strip())
+            i += 1
+        elif re.match("nxforms", lta[i]) is not None:
+            d["nxforms"] = int(re.sub("=", "", re.sub("[a-z]+", "", re.sub("#.*", "", lta[i]))).strip())
+            i += 1
+        elif re.match("mean", lta[i]) is not None:
+            d["mean"] = [
+                float(x)
+                for x in re.split(" +", re.sub("=", "", re.sub("[a-z]+", "", re.sub("#.*", "", lta[i]))).strip())
+            ]
+            i += 1
+        elif re.match("sigma", lta[i]) is not None:
+            d["sigma"] = float(re.sub("=", "", re.sub("[a-z]+", "", re.sub("#.*", "", lta[i]))).strip())
+            i += 1
+        elif re.match("-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+", lta[i]) is not None:
+            d["lta"] = np.array(
+                [
+                    [
+                        float(x)
+                        for x in re.split(
+                            " +",
+                            re.match(
+                                "-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+", lta[i]
+                            ).string.strip(),
+                        )
+                    ],
+                    [
+                        float(x)
+                        for x in re.split(
+                            " +",
+                            re.match(
+                                "-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+", lta[i + 1]
+                            ).string.strip(),
+                        )
+                    ],
+                    [
+                        float(x)
+                        for x in re.split(
+                            " +",
+                            re.match(
+                                "-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+", lta[i + 2]
+                            ).string.strip(),
+                        )
+                    ],
+                    [
+                        float(x)
+                        for x in re.split(
+                            " +",
+                            re.match(
+                                "-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+-*[0-9]\.\S+\W+", lta[i + 3]
+                            ).string.strip(),
+                        )
+                    ],
+                ]
+            )
+            i += 4
+        elif re.match("src volume info", lta[i]) is not None:
+            while i < len(lta) and re.match("dst volume info", lta[i]) is None:
+                if re.match("valid", lta[i]) is not None:
+                    d["src_valid"] = int(re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                elif re.match("filename", lta[i]) is not None:
+                    d["src_filename"] = re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                elif re.match("volume", lta[i]) is not None:
+                    d["src_volume"] = [
+                        int(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("voxelsize", lta[i]) is not None:
+                    d["src_voxelsize"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("xras", lta[i]) is not None:
+                    d["src_xras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("yras", lta[i]) is not None:
+                    d["src_yras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("zras", lta[i]) is not None:
+                    d["src_zras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("cras", lta[i]) is not None:
+                    d["src_cras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                i += 1
+        elif re.match("dst volume info", lta[i]) is not None:
+            while i < len(lta) and re.match("src volume info", lta[i]) is None:
+                if re.match("valid", lta[i]) is not None:
+                    d["dst_valid"] = int(re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                elif re.match("filename", lta[i]) is not None:
+                    d["dst_filename"] = re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                elif re.match("volume", lta[i]) is not None:
+                    d["dst_volume"] = [
+                        int(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("voxelsize", lta[i]) is not None:
+                    d["dst_voxelsize"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("xras", lta[i]) is not None:
+                    d["dst_xras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("yras", lta[i]) is not None:
+                    d["dst_yras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("zras", lta[i]) is not None:
+                    d["dst_zras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                elif re.match("cras", lta[i]) is not None:
+                    d["dst_cras"] = [
+                        float(x) for x in re.split(" +", re.sub(".*=", "", re.sub("#.*", "", lta[i])).strip())
+                    ]
+                i += 1
+        else:
+            i += 1
+    # create full transformation matrices
+    d["src"] = np.concatenate(
+        (
+            np.concatenate(
+                (np.c_[d["src_xras"]], np.c_[d["src_yras"]], np.c_[d["src_zras"]], np.c_[d["src_cras"]]), axis=1
+            ),
+            np.array([0.0, 0.0, 0.0, 1.0], ndmin=2),
+        ),
+        axis=0,
+    )
+    d["dst"] = np.concatenate(
+        (
+            np.concatenate(
+                (np.c_[d["dst_xras"]], np.c_[d["dst_yras"]], np.c_[d["dst_zras"]], np.c_[d["dst_cras"]]), axis=1
+            ),
+            np.array([0.0, 0.0, 0.0, 1.0], ndmin=2),
+        ),
+        axis=0,
+    )
+    # return
+    return d
+
+
+# ------------------------------------------------------------------------------
+
+
+def levelsetsTria(v, t, p, levelsets):
+    """
+    This is the levelsetsTria function
+
+    """
+
+    import numpy as np
+    from scipy.sparse import lil_matrix
+
+    vLVL = list()
+    lLVL = list()
+    iLVL = list()
+
+    levelsets = np.array(levelsets, ndmin=2)
+
+    for lidx in range(len(levelsets)):
+        A = lil_matrix((np.shape(v)[0], np.shape(v)[0]))
+
+        lvl = levelsets[lidx]
+
+        nlvl = p[t] > lvl
+
+        n = np.where(np.logical_or(np.sum(nlvl, axis=1) == 1, np.sum(nlvl, axis=1) == 2))[0]
+
+        # interpolate points
+
+        ti = list()
+        vi = list()
+
+        for i in range(len(n)):
+            # which are the outlying points in the current tria?
+            oi = np.where(nlvl[n[i], :])[0]
+
+            #  convert 2 --> 1
+            if len(oi) == 2:
+                oi = np.setdiff1d((0, 1, 2), oi)
+
+            # find the two non - outyling points
+            oix = np.setdiff1d((0, 1, 2), oi)
+
+            # check if we have interpolated for one or both of these points before
+            if np.count_nonzero(A[t[n[i], oi.item()], t[n[i], oix[0]]]) == 0:
+                # compute difference vectors between outlying point and other points
+
+                d10 = v[t[n[i], oix[0]], :] - v[t[n[i], oi], :]
+
+                # compute differences of all points to lvl to get interpolation factors
+
+                s10 = (lvl - p[t[n[i], oi]]) / (p[t[n[i], oix[0]]] - p[t[n[i], oi]])
+
+                # compute new points
+
+                v10 = s10 * d10 + v[t[n[i], oi], :]
+
+                # update vi and index(order matters)
+
+                vi.append(v10.tolist()[0])
+
+                ti10 = len(vi)
+
+                # store between which two points we are interpolating (to avoid having duplicate points)
+
+                A[t[n[i], oi.item()], t[n[i], oix[0]]] = ti10
+                A[t[n[i], oix[0]], t[n[i], oi.item()]] = ti10
+
+            else:
+                ti10 = int(A[t[n[i], oi.item()], t[n[i], oix[0]]])
+
+            # essentially the same as above, just for oix[1]
+
+            if np.count_nonzero(A[t[n[i], oi.item()], t[n[i], oix[1]]]) == 0:
+                d20 = v[t[n[i], oix[1]], :] - v[t[n[i], oi], :]
+
+                s20 = (lvl - p[t[n[i], oi]]) / (p[t[n[i], oix[1]]] - p[t[n[i], oi]])
+
+                v20 = s20 * d20 + v[t[n[i], oi], :]
+
+                # update vi and index(order matters)
+
+                vi.append(v20.tolist()[0])
+
+                ti20 = len(vi)
+
+                A[t[n[i], oi.item()], t[n[i], oix[1]]] = ti20
+                A[t[n[i], oix[1]], t[n[i], oi.item()]] = ti20
+
+            else:
+                ti20 = int(A[t[n[i], oi.item()], t[n[i], oix[1]]])
+
+            # store new indices
+
+            ti.append((ti10, ti20))
+
+            # clean up
+
+            # clear oi oix d10 d20 s10 s20 v10 v20 t10 t20
+
+        # store
+
+        vLVL.append(vi)
+        lLVL.append(ti)
+        iLVL.append(n)
+
+    return vLVL, lLVL, iLVL
+
+# ------------------------------------------------------------------------------
+
+def returnFreeSurferColorLUT():
+    """
+    Provides FreeSurfer color look-up table
+    """
+
+    import numpy as np
+
+    return np.array((
+        [0,"Unknown",0,0,0,0],
+        [1,"Left-Cerebral-Exterior",70,130,180,0],
+        [2,"Left-Cerebral-White-Matter",245,245,245,0],
+        [3,"Left-Cerebral-Cortex",205,62,78,0],
+        [4,"Left-Lateral-Ventricle",120,18,134,0],
+        [5,"Left-Inf-Lat-Vent",196,58,250,0],
+        [6,"Left-Cerebellum-Exterior",0,148,0,0],
+        [7,"Left-Cerebellum-White-Matter",220,248,164,0],
+        [8,"Left-Cerebellum-Cortex",230,148,34,0],
+        [9,"Left-Thalamus-unused",0,118,14,0],
+        [10,"Left-Thalamus",0,118,14,0],
+        [11,"Left-Caudate",122,186,220,0],
+        [12,"Left-Putamen",236,13,176,0],
+        [13,"Left-Pallidum",12,48,255,0],
+        [14,"3rd-Ventricle",204,182,142,0],
+        [15,"4th-Ventricle",42,204,164,0],
+        [16,"Brain-Stem",119,159,176,0],
+        [17,"Left-Hippocampus",220,216,20,0],
+        [18,"Left-Amygdala",103,255,255,0],
+        [19,"Left-Insula",80,196,98,0],
+        [20,"Left-Operculum",60,58,210,0],
+        [21,"Line-1",60,58,210,0],
+        [22,"Line-2",60,58,210,0],
+        [23,"Line-3",60,58,210,0],
+        [24,"CSF",60,60,60,0],
+        [25,"Left-Lesion",255,165,0,0],
+        [26,"Left-Accumbens-area",255,165,0,0],
+        [27,"Left-Substancia-Nigra",0,255,127,0],
+        [28,"Left-VentralDC",165,42,42,0],
+        [29,"Left-undetermined",135,206,235,0],
+        [30,"Left-vessel",160,32,240,0],
+        [31,"Left-choroid-plexus",0,200,200,0],
+        [32,"Left-F3orb",100,50,100,0],
+        [33,"Left-lOg",135,50,74,0],
+        [34,"Left-aOg",122,135,50,0],
+        [35,"Left-mOg",51,50,135,0],
+        [36,"Left-pOg",74,155,60,0],
+        [37,"Left-Stellate",120,62,43,0],
+        [38,"Left-Porg",74,155,60,0],
+        [39,"Left-Aorg",122,135,50,0],
+        [40,"Right-Cerebral-Exterior",70,130,180,0],
+        [41,"Right-Cerebral-White-Matter",245,245,245,0],
+        [42,"Right-Cerebral-Cortex",205,62,78,0],
+        [43,"Right-Lateral-Ventricle",120,18,134,0],
+        [44,"Right-Inf-Lat-Vent",196,58,250,0],
+        [45,"Right-Cerebellum-Exterior",0,148,0,0],
+        [46,"Right-Cerebellum-White-Matter",220,248,164,0],
+        [47,"Right-Cerebellum-Cortex",230,148,34,0],
+        [48,"Right-Thalamus-unused",0,118,14,0],
+        [49,"Right-Thalamus",0,118,14,0],
+        [50,"Right-Caudate",122,186,220,0],
+        [51,"Right-Putamen",236,13,176,0],
+        [52,"Right-Pallidum",13,48,255,0],
+        [53,"Right-Hippocampus",220,216,20,0],
+        [54,"Right-Amygdala",103,255,255,0],
+        [55,"Right-Insula",80,196,98,0],
+        [56,"Right-Operculum",60,58,210,0],
+        [57,"Right-Lesion",255,165,0,0],
+        [58,"Right-Accumbens-area",255,165,0,0],
+        [59,"Right-Substancia-Nigra",0,255,127,0],
+        [60,"Right-VentralDC",165,42,42,0],
+        [61,"Right-undetermined",135,206,235,0],
+        [62,"Right-vessel",160,32,240,0],
+        [63,"Right-choroid-plexus",0,200,221,0],
+        [64,"Right-F3orb",100,50,100,0],
+        [65,"Right-lOg",135,50,74,0],
+        [66,"Right-aOg",122,135,50,0],
+        [67,"Right-mOg",51,50,135,0],
+        [68,"Right-pOg",74,155,60,0],
+        [69,"Right-Stellate",120,62,43,0],
+        [70,"Right-Porg",74,155,60,0],
+        [71,"Right-Aorg",122,135,50,0],
+        [72,"5th-Ventricle",120,190,150,0],
+        [73,"Left-Interior",122,135,50,0],
+        [74,"Right-Interior",122,135,50,0],
+        [77,"WM-hypointensities",200,70,255,0],
+        [78,"Left-WM-hypointensities",255,148,10,0],
+        [79,"Right-WM-hypointensities",255,148,10,0],
+        [80,"non-WM-hypointensities",164,108,226,0],
+        [81,"Left-non-WM-hypointensities",164,108,226,0],
+        [82,"Right-non-WM-hypointensities",164,108,226,0],
+        [83,"Left-F1",255,218,185,0],
+        [84,"Right-F1",255,218,185,0],
+        [85,"Optic-Chiasm",234,169,30,0],
+        [192,"Corpus_Callosum",250,255,50,0],
+        [86,"Left_future_WMSA",200,120,255,0],
+        [87,"Right_future_WMSA",200,121,255,0],
+        [88,"future_WMSA",200,122,255,0],
+        [96,"Left-Amygdala-Anterior",205,10,125,0],
+        [97,"Right-Amygdala-Anterior",205,10,125,0],
+        [98,"Dura",160,32,240,0],
+        [99,"Lesion",255,165,0,0],
+        [100,"Left-wm-intensity-abnormality",124,140,178,0],
+        [101,"Left-caudate-intensity-abnormality",125,140,178,0],
+        [102,"Left-putamen-intensity-abnormality",126,140,178,0],
+        [103,"Left-accumbens-intensity-abnormality",127,140,178,0],
+        [104,"Left-pallidum-intensity-abnormality",124,141,178,0],
+        [105,"Left-amygdala-intensity-abnormality",124,142,178,0],
+        [106,"Left-hippocampus-intensity-abnormality",124,143,178,0],
+        [107,"Left-thalamus-intensity-abnormality",124,144,178,0],
+        [108,"Left-VDC-intensity-abnormality",124,140,179,0],
+        [109,"Right-wm-intensity-abnormality",124,140,178,0],
+        [110,"Right-caudate-intensity-abnormality",125,140,178,0],
+        [111,"Right-putamen-intensity-abnormality",126,140,178,0],
+        [112,"Right-accumbens-intensity-abnormality",127,140,178,0],
+        [113,"Right-pallidum-intensity-abnormality",124,141,178,0],
+        [114,"Right-amygdala-intensity-abnormality",124,142,178,0],
+        [115,"Right-hippocampus-intensity-abnormality",124,143,178,0],
+        [116,"Right-thalamus-intensity-abnormality",124,144,178,0],
+        [117,"Right-VDC-intensity-abnormality",124,140,179,0],
+        [118,"Epidermis",255,20,147,0],
+        [119,"Conn-Tissue",205,179,139,0],
+        [120,"SC-Fat-Muscle",238,238,209,0],
+        [121,"Cranium",200,200,200,0],
+        [122,"CSF-SA",74,255,74,0],
+        [123,"Muscle",238,0,0,0],
+        [124,"Ear",0,0,139,0],
+        [125,"Adipose",173,255,47,0],
+        [126,"Spinal-Cord",133,203,229,0],
+        [127,"Soft-Tissue",26,237,57,0],
+        [128,"Nerve",34,139,34,0],
+        [129,"Bone",30,144,255,0],
+        [130,"AirCavity",196,160,128,0],
+        [131,"Orbital-Fat",238,59,59,0],
+        [132,"Tongue",221,39,200,0],
+        [133,"Nasal-Structures",238,174,238,0],
+        [134,"Globe",255,0,0,0],
+        [135,"Teeth",72,61,139,0],
+        [136,"Left-Caudate-Putamen",21,39,132,0],
+        [137,"Right-Caudate-Putamen",21,39,132,0],
+        [138,"Left-Claustrum",65,135,20,0],
+        [139,"Right-Claustrum",65,135,20,0],
+        [140,"Cornea",134,4,160,0],
+        [142,"Diploe",221,226,68,0],
+        [143,"Vitreous-Humor",255,255,254,0],
+        [144,"Lens",52,209,226,0],
+        [145,"Aqueous-Humor",239,160,223,0],
+        [146,"Outer-Table",70,130,180,0],
+        [147,"Inner-Table",70,130,181,0],
+        [148,"Periosteum",139,121,94,0],
+        [149,"Endosteum",224,224,224,0],
+        [150,"R-C-S",255,0,0,0],
+        [151,"Iris",205,205,0,0],
+        [152,"SC-Adipose-Muscle",238,238,209,0],
+        [153,"SC-Tissue",139,121,94,0],
+        [154,"Orbital-Adipose",238,59,59,0],
+        [155,"Left-IntCapsule-Ant",238,59,59,0],
+        [156,"Right-IntCapsule-Ant",238,59,59,0],
+        [157,"Left-IntCapsule-Pos",62,10,205,0],
+        [158,"Right-IntCapsule-Pos",62,10,205,0],
+        [159,"Left-Cerebral-WM-unmyelinated",0,118,14,0],
+        [160,"Right-Cerebral-WM-unmyelinated",0,118,14,0],
+        [161,"Left-Cerebral-WM-myelinated",220,216,21,0],
+        [162,"Right-Cerebral-WM-myelinated",220,216,21,0],
+        [163,"Left-Subcortical-Gray-Matter",122,186,220,0],
+        [164,"Right-Subcortical-Gray-Matter",122,186,220,0],
+        [165,"Skull",120,120,120,0],
+        [166,"Posterior-fossa",14,48,255,0],
+        [167,"Scalp",166,42,42,0],
+        [168,"Hematoma",121,18,134,0],
+        [169,"Left-Basal-Ganglia",236,13,127,0],
+        [176,"Right-Basal-Ganglia",236,13,126,0],
+        [170,"brainstem",119,159,176,0],
+        [171,"DCG",119,0,176,0],
+        [172,"Vermis",119,100,176,0],
+        [173,"Midbrain",242,104,76,0],
+        [174,"Pons",206,195,58,0],
+        [175,"Medulla",119,159,176,0],
+        [177,"Vermis-White-Matter",119,50,176,0],
+        [178,"SCP",142,182,0,0],
+        [179,"Floculus",19,100,176,0],
+        [180,"Left-Cortical-Dysplasia",73,61,139,0],
+        [181,"Right-Cortical-Dysplasia",73,62,139,0],
+        [182,"CblumNodulus",10,100,176,0],
+        [193,"Left-hippocampal_fissure",0,196,255,0],
+        [194,"Left-CADG-head",255,164,164,0],
+        [195,"Left-subiculum",196,196,0,0],
+        [196,"Left-fimbria",0,100,255,0],
+        [197,"Right-hippocampal_fissure",128,196,164,0],
+        [198,"Right-CADG-head",0,126,75,0],
+        [199,"Right-subiculum",128,96,64,0],
+        [200,"Right-fimbria",0,50,128,0],
+        [201,"alveus",255,204,153,0],
+        [202,"perforant_pathway",255,128,128,0],
+        [203,"parasubiculum",175,175,75,0],
+        [204,"presubiculum",64,0,64,0],
+        [205,"subiculum",0,0,255,0],
+        [206,"CA1",255,0,0,0],
+        [207,"CA2",128,128,255,0],
+        [208,"CA3",0,128,0,0],
+        [209,"CA4",196,160,128,0],
+        [210,"GC-DG",32,200,255,0],
+        [211,"HATA",128,255,128,0],
+        [212,"fimbria",204,153,204,0],
+        [213,"lateral_ventricle",121,17,136,0],
+        [214,"molecular_layer_HP",128,0,0,0],
+        [215,"hippocampal_fissure",128,32,255,0],
+        [216,"entorhinal_cortex",255,204,102,0],
+        [217,"molecular_layer_subiculum",128,128,128,0],
+        [218,"Amygdala",104,255,255,0],
+        [219,"Cerebral_White_Matter",0,226,0,0],
+        [220,"Cerebral_Cortex",205,63,78,0],
+        [221,"Inf_Lat_Vent",197,58,250,0],
+        [222,"Perirhinal",33,150,250,0],
+        [223,"Cerebral_White_Matter_Edge",226,0,0,0],
+        [224,"Background",100,100,100,0],
+        [225,"Ectorhinal",197,150,250,0],
+        [226,"HP_tail",170,170,255,0],
+        [227,"Polymorphic-Layer",128,255,128,0],
+        [228,"Intracellular-Space",204,153,204,0],
+        [229,"molecular_layer_DG",168,0,0,0],
+        [231,"HP_body",0,255,0,0],
+        [232,"HP_head",255,0,0,0],
+        [233,"presubiculum-head",32,0,32,0],
+        [234,"presubiculum-body",64,0,64,0],
+        [235,"subiculum-head",0,0,175,0],
+        [236,"subiculum-body",0,0,255,0],
+        [237,"CA1-head",175,75,75,0],
+        [238,"CA1-body",255,0,0,0],
+        [239,"CA3-head",0,80,0,0],
+        [240,"CA3-body",0,128,0,0],
+        [241,"CA4-head",120,90,50,0],
+        [242,"CA4-body",196,160,128,0],
+        [243,"GC-ML-DG-head",75,125,175,0],
+        [244,"GC-ML-DG-body",32,200,255,0],
+        [245,"molecular_layer_HP-head",100,25,25,0],
+        [246,"molecular_layer_HP-body",128,0,0,0],
+        [247,"FreezeSurface",10,100,100,0],
+        [250,"Fornix",255,0,0,0],
+        [251,"CC_Posterior",0,0,64,0],
+        [252,"CC_Mid_Posterior",0,0,112,0],
+        [253,"CC_Central",0,0,160,0],
+        [254,"CC_Mid_Anterior",0,0,208,0],
+        [255,"CC_Anterior",0,0,255,0],
+        [256,"Voxel-Unchanged",0,0,0,0],
+        [257,"CSF-ExtraCerebral",60,60,60,0],
+        [258,"Head-ExtraCerebral",150,150,200,0],
+        [259,"Eye-Fluid",60,60,60,0],
+        [260,"BoneOrAir",119,159,176,0],
+        [261,"PossibleFluid",120,18,134,0],
+        [262,"Sinus",196,160,128,0],
+        [263,"Left-Eustachian",119,159,176,0],
+        [264,"Right-Eustachian",119,159,176,0],
+        [265,"Left-Eyeball",60,60,60,0],
+        [266,"Right-Eyeball",60,60,60,0],
+        [331,"Aorta",255,0,0,0],
+        [332,"Left-Common-IliacA",255,80,0,0],
+        [333,"Right-Common-IliacA",255,160,0,0],
+        [334,"Left-External-IliacA",255,255,0,0],
+        [335,"Right-External-IliacA",0,255,0,0],
+        [336,"Left-Internal-IliacA",255,0,160,0],
+        [337,"Right-Internal-IliacA",255,0,255,0],
+        [338,"Left-Lateral-SacralA",255,50,80,0],
+        [339,"Right-Lateral-SacralA",80,255,50,0],
+        [340,"Left-ObturatorA",160,255,50,0],
+        [341,"Right-ObturatorA",160,200,255,0],
+        [342,"Left-Internal-PudendalA",0,255,160,0],
+        [343,"Right-Internal-PudendalA",0,0,255,0],
+        [344,"Left-UmbilicalA",80,50,255,0],
+        [345,"Right-UmbilicalA",160,0,255,0],
+        [346,"Left-Inf-RectalA",255,210,0,0],
+        [347,"Right-Inf-RectalA",0,160,255,0],
+        [348,"Left-Common-IliacV",255,200,80,0],
+        [349,"Right-Common-IliacV",255,200,160,0],
+        [350,"Left-External-IliacV",255,80,200,0],
+        [351,"Right-External-IliacV",255,160,200,0],
+        [352,"Left-Internal-IliacV",30,255,80,0],
+        [353,"Right-Internal-IliacV",80,200,255,0],
+        [354,"Left-ObturatorV",80,255,200,0],
+        [355,"Right-ObturatorV",195,255,200,0],
+        [356,"Left-Internal-PudendalV",120,200,20,0],
+        [357,"Right-Internal-PudendalV",170,10,200,0],
+        [358,"Pos-Lymph",20,130,180,0],
+        [359,"Neg-Lymph",20,180,130,0],
+        [400,"V1",206,62,78,0],
+        [401,"V2",121,18,134,0],
+        [402,"BA44",199,58,250,0],
+        [403,"BA45",1,148,0,0],
+        [404,"BA4a",221,248,164,0],
+        [405,"BA4p",231,148,34,0],
+        [406,"BA6",1,118,14,0],
+        [407,"BA2",120,118,14,0],
+        [408,"BA1_old",123,186,221,0],
+        [409,"BAun2",238,13,177,0],
+        [410,"BA1",123,186,220,0],
+        [411,"BA2b",138,13,206,0],
+        [412,"BA3a",238,130,176,0],
+        [413,"BA3b",218,230,76,0],
+        [414,"MT",38,213,176,0],
+        [415,"AIPS_AIP_l",1,225,176,0],
+        [416,"AIPS_AIP_r",1,225,176,0],
+        [417,"AIPS_VIP_l",200,2,100,0],
+        [418,"AIPS_VIP_r",200,2,100,0],
+        [419,"IPL_PFcm_l",5,200,90,0],
+        [420,"IPL_PFcm_r",5,200,90,0],
+        [421,"IPL_PF_l",100,5,200,0],
+        [422,"IPL_PFm_l",25,255,100,0],
+        [423,"IPL_PFm_r",25,255,100,0],
+        [424,"IPL_PFop_l",230,7,100,0],
+        [425,"IPL_PFop_r",230,7,100,0],
+        [426,"IPL_PF_r",100,5,200,0],
+        [427,"IPL_PFt_l",150,10,200,0],
+        [428,"IPL_PFt_r",150,10,200,0],
+        [429,"IPL_PGa_l",175,10,176,0],
+        [430,"IPL_PGa_r",175,10,176,0],
+        [431,"IPL_PGp_l",10,100,255,0],
+        [432,"IPL_PGp_r",10,100,255,0],
+        [433,"Visual_V3d_l",150,45,70,0],
+        [434,"Visual_V3d_r",150,45,70,0],
+        [435,"Visual_V4_l",45,200,15,0],
+        [436,"Visual_V4_r",45,200,15,0],
+        [437,"Visual_V5_b",227,45,100,0],
+        [438,"Visual_VP_l",227,45,100,0],
+        [439,"Visual_VP_r",227,45,100,0],
+        [498,"wmsa",143,188,143,0],
+        [499,"other_wmsa",255,248,220,0],
+        [500,"right_CA2_3",17,85,136,0],
+        [501,"right_alveus",119,187,102,0],
+        [502,"right_CA1",204,68,34,0],
+        [503,"right_fimbria",204,0,255,0],
+        [504,"right_presubiculum",221,187,17,0],
+        [505,"right_hippocampal_fissure",153,221,238,0],
+        [506,"right_CA4_DG",51,17,17,0],
+        [507,"right_subiculum",0,119,85,0],
+        [508,"right_fornix",20,100,200,0],
+        [550,"left_CA2_3",17,85,137,0],
+        [551,"left_alveus",119,187,103,0],
+        [552,"left_CA1",204,68,35,0],
+        [553,"left_fimbria",204,0,254,0],
+        [554,"left_presubiculum",221,187,16,0],
+        [555,"left_hippocampal_fissure",153,221,239,0],
+        [556,"left_CA4_DG",51,17,18,0],
+        [557,"left_subiculum",0,119,86,0],
+        [558,"left_fornix",20,100,201,0],
+        [600,"Tumor",254,254,254,0],
+        [601,"Cbm_Left_I_IV",70,130,180,0],
+        [602,"Cbm_Right_I_IV",245,245,245,0],
+        [603,"Cbm_Left_V",205,62,78,0],
+        [604,"Cbm_Right_V",120,18,134,0],
+        [605,"Cbm_Left_VI",196,58,250,0],
+        [606,"Cbm_Vermis_VI",0,148,0,0],
+        [607,"Cbm_Right_VI",220,248,164,0],
+        [608,"Cbm_Left_CrusI",230,148,34,0],
+        [609,"Cbm_Vermis_CrusI",0,118,14,0],
+        [610,"Cbm_Right_CrusI",0,118,14,0],
+        [611,"Cbm_Left_CrusII",122,186,220,0],
+        [612,"Cbm_Vermis_CrusII",236,13,176,0],
+        [613,"Cbm_Right_CrusII",12,48,255,0],
+        [614,"Cbm_Left_VIIb",204,182,142,0],
+        [615,"Cbm_Vermis_VIIb",42,204,164,0],
+        [616,"Cbm_Right_VIIb",119,159,176,0],
+        [617,"Cbm_Left_VIIIa",220,216,20,0],
+        [618,"Cbm_Vermis_VIIIa",103,255,255,0],
+        [619,"Cbm_Right_VIIIa",80,196,98,0],
+        [620,"Cbm_Left_VIIIb",60,58,210,0],
+        [621,"Cbm_Vermis_VIIIb",60,58,210,0],
+        [622,"Cbm_Right_VIIIb",60,58,210,0],
+        [623,"Cbm_Left_IX",60,58,210,0],
+        [624,"Cbm_Vermis_IX",60,60,60,0],
+        [625,"Cbm_Right_IX",255,165,0,0],
+        [626,"Cbm_Left_X",255,165,0,0],
+        [627,"Cbm_Vermis_X",0,255,127,0],
+        [628,"Cbm_Right_X",165,42,42,0],
+        [640,"Cbm_Right_I_V_med",204,0,0,0],
+        [641,"Cbm_Right_I_V_mid",255,0,0,0],
+        [642,"Cbm_Right_VI_med",0,0,255,0],
+        [643,"Cbm_Right_VI_mid",30,144,255,0],
+        [644,"Cbm_Right_VI_lat",100,212,237,0],
+        [645,"Cbm_Right_CrusI_med",218,165,32,0],
+        [646,"Cbm_Right_CrusI_mid",255,215,0,0],
+        [647,"Cbm_Right_CrusI_lat",255,255,166,0],
+        [648,"Cbm_Right_CrusII_med",153,0,204,0],
+        [649,"Cbm_Right_CrusII_mid",153,141,209,0],
+        [650,"Cbm_Right_CrusII_lat",204,204,255,0],
+        [651,"Cbm_Right_7med",31,212,194,0],
+        [652,"Cbm_Right_7mid",3,255,237,0],
+        [653,"Cbm_Right_7lat",204,255,255,0],
+        [654,"Cbm_Right_8med",86,74,147,0],
+        [655,"Cbm_Right_8mid",114,114,190,0],
+        [656,"Cbm_Right_8lat",184,178,255,0],
+        [657,"Cbm_Right_PUNs",126,138,37,0],
+        [658,"Cbm_Right_TONs",189,197,117,0],
+        [659,"Cbm_Right_FLOs",240,230,140,0],
+        [660,"Cbm_Left_I_V_med",204,0,0,0],
+        [661,"Cbm_Left_I_V_mid",255,0,0,0],
+        [662,"Cbm_Left_VI_med",0,0,255,0],
+        [663,"Cbm_Left_VI_mid",30,144,255,0],
+        [664,"Cbm_Left_VI_lat",100,212,237,0],
+        [665,"Cbm_Left_CrusI_med",218,165,32,0],
+        [666,"Cbm_Left_CrusI_mid",255,215,0,0],
+        [667,"Cbm_Left_CrusI_lat",255,255,166,0],
+        [668,"Cbm_Left_CrusII_med",153,0,204,0],
+        [669,"Cbm_Left_CrusII_mid",153,141,209,0],
+        [670,"Cbm_Left_CrusII_lat",204,204,255,0],
+        [671,"Cbm_Left_7med",31,212,194,0],
+        [672,"Cbm_Left_7mid",3,255,237,0],
+        [673,"Cbm_Left_7lat",204,255,255,0],
+        [674,"Cbm_Left_8med",86,74,147,0],
+        [675,"Cbm_Left_8mid",114,114,190,0],
+        [676,"Cbm_Left_8lat",184,178,255,0],
+        [677,"Cbm_Left_PUNs",126,138,37,0],
+        [678,"Cbm_Left_TONs",189,197,117,0],
+        [679,"Cbm_Left_FLOs",240,230,140,0],
+        [690,"CbmWM_Gyri_Left",122,135,50,0],
+        [691,"CbmWM_Gyri_Right",122,135,50,0],
+        [701,"CSF-FSL-FAST",120,18,134,0],
+        [702,"GrayMatter-FSL-FAST",205,62,78,0],
+        [703,"WhiteMatter-FSL-FAST",0,225,0,0],
+        [801,"L_hypothalamus_anterior_inferior",250,255,50,0],
+        [802,"L_hypothalamus_anterior_superior",80,200,255,0],
+        [803,"L_hypothalamus_posterior",255,160,0,0],
+        [804,"L_hypothalamus_tubular_inferior",255,160,200,0],
+        [805,"L_hypothalamus_tubular_superior",20,180,130,0],
+        [806,"R_hypothalamus_anterior_inferior",250,255,50,0],
+        [807,"R_hypothalamus_anterior_superior",80,200,255,0],
+        [808,"R_hypothalamus_posterior",255,160,0,0],
+        [809,"R_hypothalamus_tubular_inferior",255,160,200,0],
+        [810,"R_hypothalamus_tubular_superior",20,180,130,0],
+        [999,"SUSPICIOUS",255,100,100,0],
+        [1000,"ctx-lh-unknown",25,5,25,0],
+        [1001,"ctx-lh-bankssts",25,100,40,0],
+        [1002,"ctx-lh-caudalanteriorcingulate",125,100,160,0],
+        [1003,"ctx-lh-caudalmiddlefrontal",100,25,0,0],
+        [1004,"ctx-lh-corpuscallosum",120,70,50,0],
+        [1005,"ctx-lh-cuneus",220,20,100,0],
+        [1006,"ctx-lh-entorhinal",220,20,10,0],
+        [1007,"ctx-lh-fusiform",180,220,140,0],
+        [1008,"ctx-lh-inferiorparietal",220,60,220,0],
+        [1009,"ctx-lh-inferiortemporal",180,40,120,0],
+        [1010,"ctx-lh-isthmuscingulate",140,20,140,0],
+        [1011,"ctx-lh-lateraloccipital",20,30,140,0],
+        [1012,"ctx-lh-lateralorbitofrontal",35,75,50,0],
+        [1013,"ctx-lh-lingual",225,140,140,0],
+        [1014,"ctx-lh-medialorbitofrontal",200,35,75,0],
+        [1015,"ctx-lh-middletemporal",160,100,50,0],
+        [1016,"ctx-lh-parahippocampal",20,220,60,0],
+        [1017,"ctx-lh-paracentral",60,220,60,0],
+        [1018,"ctx-lh-parsopercularis",220,180,140,0],
+        [1019,"ctx-lh-parsorbitalis",20,100,50,0],
+        [1020,"ctx-lh-parstriangularis",220,60,20,0],
+        [1021,"ctx-lh-pericalcarine",120,100,60,0],
+        [1022,"ctx-lh-postcentral",220,20,20,0],
+        [1023,"ctx-lh-posteriorcingulate",220,180,220,0],
+        [1024,"ctx-lh-precentral",60,20,220,0],
+        [1025,"ctx-lh-precuneus",160,140,180,0],
+        [1026,"ctx-lh-rostralanteriorcingulate",80,20,140,0],
+        [1027,"ctx-lh-rostralmiddlefrontal",75,50,125,0],
+        [1028,"ctx-lh-superiorfrontal",20,220,160,0],
+        [1029,"ctx-lh-superiorparietal",20,180,140,0],
+        [1030,"ctx-lh-superiortemporal",140,220,220,0],
+        [1031,"ctx-lh-supramarginal",80,160,20,0],
+        [1032,"ctx-lh-frontalpole",100,0,100,0],
+        [1033,"ctx-lh-temporalpole",70,70,70,0],
+        [1034,"ctx-lh-transversetemporal",150,150,200,0],
+        [1035,"ctx-lh-insula",255,192,32,0],
+        [2000,"ctx-rh-unknown",25,5,25,0],
+        [2001,"ctx-rh-bankssts",25,100,40,0],
+        [2002,"ctx-rh-caudalanteriorcingulate",125,100,160,0],
+        [2003,"ctx-rh-caudalmiddlefrontal",100,25,0,0],
+        [2004,"ctx-rh-corpuscallosum",120,70,50,0],
+        [2005,"ctx-rh-cuneus",220,20,100,0],
+        [2006,"ctx-rh-entorhinal",220,20,10,0],
+        [2007,"ctx-rh-fusiform",180,220,140,0],
+        [2008,"ctx-rh-inferiorparietal",220,60,220,0],
+        [2009,"ctx-rh-inferiortemporal",180,40,120,0],
+        [2010,"ctx-rh-isthmuscingulate",140,20,140,0],
+        [2011,"ctx-rh-lateraloccipital",20,30,140,0],
+        [2012,"ctx-rh-lateralorbitofrontal",35,75,50,0],
+        [2013,"ctx-rh-lingual",225,140,140,0],
+        [2014,"ctx-rh-medialorbitofrontal",200,35,75,0],
+        [2015,"ctx-rh-middletemporal",160,100,50,0],
+        [2016,"ctx-rh-parahippocampal",20,220,60,0],
+        [2017,"ctx-rh-paracentral",60,220,60,0],
+        [2018,"ctx-rh-parsopercularis",220,180,140,0],
+        [2019,"ctx-rh-parsorbitalis",20,100,50,0],
+        [2020,"ctx-rh-parstriangularis",220,60,20,0],
+        [2021,"ctx-rh-pericalcarine",120,100,60,0],
+        [2022,"ctx-rh-postcentral",220,20,20,0],
+        [2023,"ctx-rh-posteriorcingulate",220,180,220,0],
+        [2024,"ctx-rh-precentral",60,20,220,0],
+        [2025,"ctx-rh-precuneus",160,140,180,0],
+        [2026,"ctx-rh-rostralanteriorcingulate",80,20,140,0],
+        [2027,"ctx-rh-rostralmiddlefrontal",75,50,125,0],
+        [2028,"ctx-rh-superiorfrontal",20,220,160,0],
+        [2029,"ctx-rh-superiorparietal",20,180,140,0],
+        [2030,"ctx-rh-superiortemporal",140,220,220,0],
+        [2031,"ctx-rh-supramarginal",80,160,20,0],
+        [2032,"ctx-rh-frontalpole",100,0,100,0],
+        [2033,"ctx-rh-temporalpole",70,70,70,0],
+        [2034,"ctx-rh-transversetemporal",150,150,200,0],
+        [2035,"ctx-rh-insula",255,192,32,0],
+        [3000,"wm-lh-unknown",230,250,230,0],
+        [3001,"wm-lh-bankssts",230,155,215,0],
+        [3002,"wm-lh-caudalanteriorcingulate",130,155,95,0],
+        [3003,"wm-lh-caudalmiddlefrontal",155,230,255,0],
+        [3004,"wm-lh-corpuscallosum",135,185,205,0],
+        [3005,"wm-lh-cuneus",35,235,155,0],
+        [3006,"wm-lh-entorhinal",35,235,245,0],
+        [3007,"wm-lh-fusiform",75,35,115,0],
+        [3008,"wm-lh-inferiorparietal",35,195,35,0],
+        [3009,"wm-lh-inferiortemporal",75,215,135,0],
+        [3010,"wm-lh-isthmuscingulate",115,235,115,0],
+        [3011,"wm-lh-lateraloccipital",235,225,115,0],
+        [3012,"wm-lh-lateralorbitofrontal",220,180,205,0],
+        [3013,"wm-lh-lingual",30,115,115,0],
+        [3014,"wm-lh-medialorbitofrontal",55,220,180,0],
+        [3015,"wm-lh-middletemporal",95,155,205,0],
+        [3016,"wm-lh-parahippocampal",235,35,195,0],
+        [3017,"wm-lh-paracentral",195,35,195,0],
+        [3018,"wm-lh-parsopercularis",35,75,115,0],
+        [3019,"wm-lh-parsorbitalis",235,155,205,0],
+        [3020,"wm-lh-parstriangularis",35,195,235,0],
+        [3021,"wm-lh-pericalcarine",135,155,195,0],
+        [3022,"wm-lh-postcentral",35,235,235,0],
+        [3023,"wm-lh-posteriorcingulate",35,75,35,0],
+        [3024,"wm-lh-precentral",195,235,35,0],
+        [3025,"wm-lh-precuneus",95,115,75,0],
+        [3026,"wm-lh-rostralanteriorcingulate",175,235,115,0],
+        [3027,"wm-lh-rostralmiddlefrontal",180,205,130,0],
+        [3028,"wm-lh-superiorfrontal",235,35,95,0],
+        [3029,"wm-lh-superiorparietal",235,75,115,0],
+        [3030,"wm-lh-superiortemporal",115,35,35,0],
+        [3031,"wm-lh-supramarginal",175,95,235,0],
+        [3032,"wm-lh-frontalpole",155,255,155,0],
+        [3033,"wm-lh-temporalpole",185,185,185,0],
+        [3034,"wm-lh-transversetemporal",105,105,55,0],
+        [3035,"wm-lh-insula",20,220,160,0],
+        [4000,"wm-rh-unknown",230,250,230,0],
+        [4001,"wm-rh-bankssts",230,155,215,0],
+        [4002,"wm-rh-caudalanteriorcingulate",130,155,95,0],
+        [4003,"wm-rh-caudalmiddlefrontal",155,230,255,0],
+        [4004,"wm-rh-corpuscallosum",135,185,205,0],
+        [4005,"wm-rh-cuneus",35,235,155,0],
+        [4006,"wm-rh-entorhinal",35,235,245,0],
+        [4007,"wm-rh-fusiform",75,35,115,0],
+        [4008,"wm-rh-inferiorparietal",35,195,35,0],
+        [4009,"wm-rh-inferiortemporal",75,215,135,0],
+        [4010,"wm-rh-isthmuscingulate",115,235,115,0],
+        [4011,"wm-rh-lateraloccipital",235,225,115,0],
+        [4012,"wm-rh-lateralorbitofrontal",220,180,205,0],
+        [4013,"wm-rh-lingual",30,115,115,0],
+        [4014,"wm-rh-medialorbitofrontal",55,220,180,0],
+        [4015,"wm-rh-middletemporal",95,155,205,0],
+        [4016,"wm-rh-parahippocampal",235,35,195,0],
+        [4017,"wm-rh-paracentral",195,35,195,0],
+        [4018,"wm-rh-parsopercularis",35,75,115,0],
+        [4019,"wm-rh-parsorbitalis",235,155,205,0],
+        [4020,"wm-rh-parstriangularis",35,195,235,0],
+        [4021,"wm-rh-pericalcarine",135,155,195,0],
+        [4022,"wm-rh-postcentral",35,235,235,0],
+        [4023,"wm-rh-posteriorcingulate",35,75,35,0],
+        [4024,"wm-rh-precentral",195,235,35,0],
+        [4025,"wm-rh-precuneus",95,115,75,0],
+        [4026,"wm-rh-rostralanteriorcingulate",175,235,115,0],
+        [4027,"wm-rh-rostralmiddlefrontal",180,205,130,0],
+        [4028,"wm-rh-superiorfrontal",235,35,95,0],
+        [4029,"wm-rh-superiorparietal",235,75,115,0],
+        [4030,"wm-rh-superiortemporal",115,35,35,0],
+        [4031,"wm-rh-supramarginal",175,95,235,0],
+        [4032,"wm-rh-frontalpole",155,255,155,0],
+        [4033,"wm-rh-temporalpole",185,185,185,0],
+        [4034,"wm-rh-transversetemporal",105,105,55,0],
+        [4035,"wm-rh-insula",20,220,160,0],
+        [1301,"ctx-lh-frontal-lobe",25,100,40,0],
+        [1303,"ctx-lh-cingulate-lobe",100,25,0,0],
+        [1304,"ctx-lh-occipital-lobe",120,70,50,0],
+        [1305,"ctx-lh-temporal-lobe",220,20,100,0],
+        [1306,"ctx-lh-parietal-lobe",220,20,10,0],
+        [1307,"ctx-lh-insula-lobe",255,192,32,0],
+        [2301,"ctx-rh-frontal-lobe",25,100,40,0],
+        [2303,"ctx-rh-cingulate-lobe",100,25,0,0],
+        [2304,"ctx-rh-occipital-lobe",120,70,50,0],
+        [2305,"ctx-rh-temporal-lobe",220,20,100,0],
+        [2306,"ctx-rh-parietal-lobe",220,20,10,0],
+        [2307,"ctx-rh-insula-lobe",255,192,32,0],
+        [3201,"wm-lh-frontal-lobe",235,35,95,0],
+        [3203,"wm-lh-cingulate-lobe",35,75,35,0],
+        [3204,"wm-lh-occipital-lobe",135,155,195,0],
+        [3205,"wm-lh-temporal-lobe",115,35,35,0],
+        [3206,"wm-lh-parietal-lobe",35,195,35,0],
+        [3207,"wm-lh-insula-lobe",20,220,160,0],
+        [4201,"wm-rh-frontal-lobe",235,35,95,0],
+        [4203,"wm-rh-cingulate-lobe",35,75,35,0],
+        [4204,"wm-rh-occipital-lobe",135,155,195,0],
+        [4205,"wm-rh-temporal-lobe",115,35,35,0],
+        [4206,"wm-rh-parietal-lobe",35,195,35,0],
+        [4207,"wm-rh-insula-lobe",20,220,160,0],
+        [1100,"ctx-lh-Unknown",0,0,0,0],
+        [1101,"ctx-lh-Corpus_callosum",50,50,50,0],
+        [1102,"ctx-lh-G_and_S_Insula_ONLY_AVERAGE",180,20,30,0],
+        [1103,"ctx-lh-G_cingulate-Isthmus",60,25,25,0],
+        [1104,"ctx-lh-G_cingulate-Main_part",25,60,60,0],
+        [1200,"ctx-lh-G_cingulate-caudal_ACC",25,60,61,0],
+        [1201,"ctx-lh-G_cingulate-rostral_ACC",25,90,60,0],
+        [1202,"ctx-lh-G_cingulate-posterior",25,120,60,0],
+        [1205,"ctx-lh-S_cingulate-caudal_ACC",25,150,60,0],
+        [1206,"ctx-lh-S_cingulate-rostral_ACC",25,180,60,0],
+        [1207,"ctx-lh-S_cingulate-posterior",25,210,60,0],
+        [1210,"ctx-lh-S_pericallosal-caudal",25,150,90,0],
+        [1211,"ctx-lh-S_pericallosal-rostral",25,180,90,0],
+        [1212,"ctx-lh-S_pericallosal-posterior",25,210,90,0],
+        [1105,"ctx-lh-G_cuneus",180,20,20,0],
+        [1106,"ctx-lh-G_frontal_inf-Opercular_part",220,20,100,0],
+        [1107,"ctx-lh-G_frontal_inf-Orbital_part",140,60,60,0],
+        [1108,"ctx-lh-G_frontal_inf-Triangular_part",180,220,140,0],
+        [1109,"ctx-lh-G_frontal_middle",140,100,180,0],
+        [1110,"ctx-lh-G_frontal_superior",180,20,140,0],
+        [1111,"ctx-lh-G_frontomarginal",140,20,140,0],
+        [1112,"ctx-lh-G_insular_long",21,10,10,0],
+        [1113,"ctx-lh-G_insular_short",225,140,140,0],
+        [1114,"ctx-lh-G_and_S_occipital_inferior",23,60,180,0],
+        [1115,"ctx-lh-G_occipital_middle",180,60,180,0],
+        [1116,"ctx-lh-G_occipital_superior",20,220,60,0],
+        [1117,"ctx-lh-G_occipit-temp_lat-Or_fusiform",60,20,140,0],
+        [1118,"ctx-lh-G_occipit-temp_med-Lingual_part",220,180,140,0],
+        [1119,"ctx-lh-G_occipit-temp_med-Parahippocampal_part",65,100,20,0],
+        [1120,"ctx-lh-G_orbital",220,60,20,0],
+        [1121,"ctx-lh-G_paracentral",60,100,60,0],
+        [1122,"ctx-lh-G_parietal_inferior-Angular_part",20,60,220,0],
+        [1123,"ctx-lh-G_parietal_inferior-Supramarginal_part",100,100,60,0],
+        [1124,"ctx-lh-G_parietal_superior",220,180,220,0],
+        [1125,"ctx-lh-G_postcentral",20,180,140,0],
+        [1126,"ctx-lh-G_precentral",60,140,180,0],
+        [1127,"ctx-lh-G_precuneus",25,20,140,0],
+        [1128,"ctx-lh-G_rectus",20,60,100,0],
+        [1129,"ctx-lh-G_subcallosal",60,220,20,0],
+        [1130,"ctx-lh-G_subcentral",60,20,220,0],
+        [1131,"ctx-lh-G_temporal_inferior",220,220,100,0],
+        [1132,"ctx-lh-G_temporal_middle",180,60,60,0],
+        [1133,"ctx-lh-G_temp_sup-G_temp_transv_and_interm_S",60,60,220,0],
+        [1134,"ctx-lh-G_temp_sup-Lateral_aspect",220,60,220,0],
+        [1135,"ctx-lh-G_temp_sup-Planum_polare",65,220,60,0],
+        [1136,"ctx-lh-G_temp_sup-Planum_tempolare",25,140,20,0],
+        [1137,"ctx-lh-G_and_S_transverse_frontopolar",13,0,250,0],
+        [1138,"ctx-lh-Lat_Fissure-ant_sgt-ramus_horizontal",61,20,220,0],
+        [1139,"ctx-lh-Lat_Fissure-ant_sgt-ramus_vertical",61,20,60,0],
+        [1140,"ctx-lh-Lat_Fissure-post_sgt",61,60,100,0],
+        [1141,"ctx-lh-Medial_wall",25,25,25,0],
+        [1142,"ctx-lh-Pole_occipital",140,20,60,0],
+        [1143,"ctx-lh-Pole_temporal",220,180,20,0],
+        [1144,"ctx-lh-S_calcarine",63,180,180,0],
+        [1145,"ctx-lh-S_central",221,20,10,0],
+        [1146,"ctx-lh-S_central_insula",21,220,20,0],
+        [1147,"ctx-lh-S_cingulate-Main_part_and_Intracingulate",183,100,20,0],
+        [1148,"ctx-lh-S_cingulate-Marginalis_part",221,20,100,0],
+        [1149,"ctx-lh-S_circular_insula_anterior",221,60,140,0],
+        [1150,"ctx-lh-S_circular_insula_inferior",221,20,220,0],
+        [1151,"ctx-lh-S_circular_insula_superior",61,220,220,0],
+        [1152,"ctx-lh-S_collateral_transverse_ant",100,200,200,0],
+        [1153,"ctx-lh-S_collateral_transverse_post",10,200,200,0],
+        [1154,"ctx-lh-S_frontal_inferior",221,220,20,0],
+        [1155,"ctx-lh-S_frontal_middle",141,20,100,0],
+        [1156,"ctx-lh-S_frontal_superior",61,220,100,0],
+        [1157,"ctx-lh-S_frontomarginal",21,220,60,0],
+        [1158,"ctx-lh-S_intermedius_primus-Jensen",141,60,20,0],
+        [1159,"ctx-lh-S_intraparietal-and_Parietal_transverse",143,20,220,0],
+        [1160,"ctx-lh-S_occipital_anterior",61,20,180,0],
+        [1161,"ctx-lh-S_occipital_middle_and_Lunatus",101,60,220,0],
+        [1162,"ctx-lh-S_occipital_superior_and_transversalis",21,20,140,0],
+        [1163,"ctx-lh-S_occipito-temporal_lateral",221,140,20,0],
+        [1164,"ctx-lh-S_occipito-temporal_medial_and_S_Lingual",141,100,220,0],
+        [1165,"ctx-lh-S_orbital-H_shapped",101,20,20,0],
+        [1166,"ctx-lh-S_orbital_lateral",221,100,20,0],
+        [1167,"ctx-lh-S_orbital_medial-Or_olfactory",181,200,20,0],
+        [1168,"ctx-lh-S_paracentral",21,180,140,0],
+        [1169,"ctx-lh-S_parieto_occipital",101,100,180,0],
+        [1170,"ctx-lh-S_pericallosal",181,220,20,0],
+        [1171,"ctx-lh-S_postcentral",21,140,200,0],
+        [1172,"ctx-lh-S_precentral-Inferior-part",21,20,240,0],
+        [1173,"ctx-lh-S_precentral-Superior-part",21,20,200,0],
+        [1174,"ctx-lh-S_subcentral_ant",61,180,60,0],
+        [1175,"ctx-lh-S_subcentral_post",61,180,250,0],
+        [1176,"ctx-lh-S_suborbital",21,20,60,0],
+        [1177,"ctx-lh-S_subparietal",101,60,60,0],
+        [1178,"ctx-lh-S_supracingulate",21,220,220,0],
+        [1179,"ctx-lh-S_temporal_inferior",21,180,180,0],
+        [1180,"ctx-lh-S_temporal_superior",223,220,60,0],
+        [1181,"ctx-lh-S_temporal_transverse",221,60,60,0],
+        [2100,"ctx-rh-Unknown",0,0,0,0],
+        [2101,"ctx-rh-Corpus_callosum",50,50,50,0],
+        [2102,"ctx-rh-G_and_S_Insula_ONLY_AVERAGE",180,20,30,0],
+        [2103,"ctx-rh-G_cingulate-Isthmus",60,25,25,0],
+        [2104,"ctx-rh-G_cingulate-Main_part",25,60,60,0],
+        [2105,"ctx-rh-G_cuneus",180,20,20,0],
+        [2106,"ctx-rh-G_frontal_inf-Opercular_part",220,20,100,0],
+        [2107,"ctx-rh-G_frontal_inf-Orbital_part",140,60,60,0],
+        [2108,"ctx-rh-G_frontal_inf-Triangular_part",180,220,140,0],
+        [2109,"ctx-rh-G_frontal_middle",140,100,180,0],
+        [2110,"ctx-rh-G_frontal_superior",180,20,140,0],
+        [2111,"ctx-rh-G_frontomarginal",140,20,140,0],
+        [2112,"ctx-rh-G_insular_long",21,10,10,0],
+        [2113,"ctx-rh-G_insular_short",225,140,140,0],
+        [2114,"ctx-rh-G_and_S_occipital_inferior",23,60,180,0],
+        [2115,"ctx-rh-G_occipital_middle",180,60,180,0],
+        [2116,"ctx-rh-G_occipital_superior",20,220,60,0],
+        [2117,"ctx-rh-G_occipit-temp_lat-Or_fusiform",60,20,140,0],
+        [2118,"ctx-rh-G_occipit-temp_med-Lingual_part",220,180,140,0],
+        [2119,"ctx-rh-G_occipit-temp_med-Parahippocampal_part",65,100,20,0],
+        [2120,"ctx-rh-G_orbital",220,60,20,0],
+        [2121,"ctx-rh-G_paracentral",60,100,60,0],
+        [2122,"ctx-rh-G_parietal_inferior-Angular_part",20,60,220,0],
+        [2123,"ctx-rh-G_parietal_inferior-Supramarginal_part",100,100,60,0],
+        [2124,"ctx-rh-G_parietal_superior",220,180,220,0],
+        [2125,"ctx-rh-G_postcentral",20,180,140,0],
+        [2126,"ctx-rh-G_precentral",60,140,180,0],
+        [2127,"ctx-rh-G_precuneus",25,20,140,0],
+        [2128,"ctx-rh-G_rectus",20,60,100,0],
+        [2129,"ctx-rh-G_subcallosal",60,220,20,0],
+        [2130,"ctx-rh-G_subcentral",60,20,220,0],
+        [2131,"ctx-rh-G_temporal_inferior",220,220,100,0],
+        [2132,"ctx-rh-G_temporal_middle",180,60,60,0],
+        [2133,"ctx-rh-G_temp_sup-G_temp_transv_and_interm_S",60,60,220,0],
+        [2134,"ctx-rh-G_temp_sup-Lateral_aspect",220,60,220,0],
+        [2135,"ctx-rh-G_temp_sup-Planum_polare",65,220,60,0],
+        [2136,"ctx-rh-G_temp_sup-Planum_tempolare",25,140,20,0],
+        [2137,"ctx-rh-G_and_S_transverse_frontopolar",13,0,250,0],
+        [2138,"ctx-rh-Lat_Fissure-ant_sgt-ramus_horizontal",61,20,220,0],
+        [2139,"ctx-rh-Lat_Fissure-ant_sgt-ramus_vertical",61,20,60,0],
+        [2140,"ctx-rh-Lat_Fissure-post_sgt",61,60,100,0],
+        [2141,"ctx-rh-Medial_wall",25,25,25,0],
+        [2142,"ctx-rh-Pole_occipital",140,20,60,0],
+        [2143,"ctx-rh-Pole_temporal",220,180,20,0],
+        [2144,"ctx-rh-S_calcarine",63,180,180,0],
+        [2145,"ctx-rh-S_central",221,20,10,0],
+        [2146,"ctx-rh-S_central_insula",21,220,20,0],
+        [2147,"ctx-rh-S_cingulate-Main_part_and_Intracingulate",183,100,20,0],
+        [2148,"ctx-rh-S_cingulate-Marginalis_part",221,20,100,0],
+        [2149,"ctx-rh-S_circular_insula_anterior",221,60,140,0],
+        [2150,"ctx-rh-S_circular_insula_inferior",221,20,220,0],
+        [2151,"ctx-rh-S_circular_insula_superior",61,220,220,0],
+        [2152,"ctx-rh-S_collateral_transverse_ant",100,200,200,0],
+        [2153,"ctx-rh-S_collateral_transverse_post",10,200,200,0],
+        [2154,"ctx-rh-S_frontal_inferior",221,220,20,0],
+        [2155,"ctx-rh-S_frontal_middle",141,20,100,0],
+        [2156,"ctx-rh-S_frontal_superior",61,220,100,0],
+        [2157,"ctx-rh-S_frontomarginal",21,220,60,0],
+        [2158,"ctx-rh-S_intermedius_primus-Jensen",141,60,20,0],
+        [2159,"ctx-rh-S_intraparietal-and_Parietal_transverse",143,20,220,0],
+        [2160,"ctx-rh-S_occipital_anterior",61,20,180,0],
+        [2161,"ctx-rh-S_occipital_middle_and_Lunatus",101,60,220,0],
+        [2162,"ctx-rh-S_occipital_superior_and_transversalis",21,20,140,0],
+        [2163,"ctx-rh-S_occipito-temporal_lateral",221,140,20,0],
+        [2164,"ctx-rh-S_occipito-temporal_medial_and_S_Lingual",141,100,220,0],
+        [2165,"ctx-rh-S_orbital-H_shapped",101,20,20,0],
+        [2166,"ctx-rh-S_orbital_lateral",221,100,20,0],
+        [2167,"ctx-rh-S_orbital_medial-Or_olfactory",181,200,20,0],
+        [2168,"ctx-rh-S_paracentral",21,180,140,0],
+        [2169,"ctx-rh-S_parieto_occipital",101,100,180,0],
+        [2170,"ctx-rh-S_pericallosal",181,220,20,0],
+        [2171,"ctx-rh-S_postcentral",21,140,200,0],
+        [2172,"ctx-rh-S_precentral-Inferior-part",21,20,240,0],
+        [2173,"ctx-rh-S_precentral-Superior-part",21,20,200,0],
+        [2174,"ctx-rh-S_subcentral_ant",61,180,60,0],
+        [2175,"ctx-rh-S_subcentral_post",61,180,250,0],
+        [2176,"ctx-rh-S_suborbital",21,20,60,0],
+        [2177,"ctx-rh-S_subparietal",101,60,60,0],
+        [2178,"ctx-rh-S_supracingulate",21,220,220,0],
+        [2179,"ctx-rh-S_temporal_inferior",21,180,180,0],
+        [2180,"ctx-rh-S_temporal_superior",223,220,60,0],
+        [2181,"ctx-rh-S_temporal_transverse",221,60,60,0],
+        [2200,"ctx-rh-G_cingulate-caudal_ACC",25,60,61,0],
+        [2201,"ctx-rh-G_cingulate-rostral_ACC",25,90,60,0],
+        [2202,"ctx-rh-G_cingulate-posterior",25,120,60,0],
+        [2205,"ctx-rh-S_cingulate-caudal_ACC",25,150,60,0],
+        [2206,"ctx-rh-S_cingulate-rostral_ACC",25,180,60,0],
+        [2207,"ctx-rh-S_cingulate-posterior",25,210,60,0],
+        [2210,"ctx-rh-S_pericallosal-caudal",25,150,90,0],
+        [2211,"ctx-rh-S_pericallosal-rostral",25,180,90,0],
+        [2212,"ctx-rh-S_pericallosal-posterior",25,210,90,0],
+        [3100,"wm-lh-Unknown",0,0,0,0],
+        [3101,"wm-lh-Corpus_callosum",50,50,50,0],
+        [3102,"wm-lh-G_and_S_Insula_ONLY_AVERAGE",180,20,30,0],
+        [3103,"wm-lh-G_cingulate-Isthmus",60,25,25,0],
+        [3104,"wm-lh-G_cingulate-Main_part",25,60,60,0],
+        [3105,"wm-lh-G_cuneus",180,20,20,0],
+        [3106,"wm-lh-G_frontal_inf-Opercular_part",220,20,100,0],
+        [3107,"wm-lh-G_frontal_inf-Orbital_part",140,60,60,0],
+        [3108,"wm-lh-G_frontal_inf-Triangular_part",180,220,140,0],
+        [3109,"wm-lh-G_frontal_middle",140,100,180,0],
+        [3110,"wm-lh-G_frontal_superior",180,20,140,0],
+        [3111,"wm-lh-G_frontomarginal",140,20,140,0],
+        [3112,"wm-lh-G_insular_long",21,10,10,0],
+        [3113,"wm-lh-G_insular_short",225,140,140,0],
+        [3114,"wm-lh-G_and_S_occipital_inferior",23,60,180,0],
+        [3115,"wm-lh-G_occipital_middle",180,60,180,0],
+        [3116,"wm-lh-G_occipital_superior",20,220,60,0],
+        [3117,"wm-lh-G_occipit-temp_lat-Or_fusiform",60,20,140,0],
+        [3118,"wm-lh-G_occipit-temp_med-Lingual_part",220,180,140,0],
+        [3119,"wm-lh-G_occipit-temp_med-Parahippocampal_part",65,100,20,0],
+        [3120,"wm-lh-G_orbital",220,60,20,0],
+        [3121,"wm-lh-G_paracentral",60,100,60,0],
+        [3122,"wm-lh-G_parietal_inferior-Angular_part",20,60,220,0],
+        [3123,"wm-lh-G_parietal_inferior-Supramarginal_part",100,100,60,0],
+        [3124,"wm-lh-G_parietal_superior",220,180,220,0],
+        [3125,"wm-lh-G_postcentral",20,180,140,0],
+        [3126,"wm-lh-G_precentral",60,140,180,0],
+        [3127,"wm-lh-G_precuneus",25,20,140,0],
+        [3128,"wm-lh-G_rectus",20,60,100,0],
+        [3129,"wm-lh-G_subcallosal",60,220,20,0],
+        [3130,"wm-lh-G_subcentral",60,20,220,0],
+        [3131,"wm-lh-G_temporal_inferior",220,220,100,0],
+        [3132,"wm-lh-G_temporal_middle",180,60,60,0],
+        [3133,"wm-lh-G_temp_sup-G_temp_transv_and_interm_S",60,60,220,0],
+        [3134,"wm-lh-G_temp_sup-Lateral_aspect",220,60,220,0],
+        [3135,"wm-lh-G_temp_sup-Planum_polare",65,220,60,0],
+        [3136,"wm-lh-G_temp_sup-Planum_tempolare",25,140,20,0],
+        [3137,"wm-lh-G_and_S_transverse_frontopolar",13,0,250,0],
+        [3138,"wm-lh-Lat_Fissure-ant_sgt-ramus_horizontal",61,20,220,0],
+        [3139,"wm-lh-Lat_Fissure-ant_sgt-ramus_vertical",61,20,60,0],
+        [3140,"wm-lh-Lat_Fissure-post_sgt",61,60,100,0],
+        [3141,"wm-lh-Medial_wall",25,25,25,0],
+        [3142,"wm-lh-Pole_occipital",140,20,60,0],
+        [3143,"wm-lh-Pole_temporal",220,180,20,0],
+        [3144,"wm-lh-S_calcarine",63,180,180,0],
+        [3145,"wm-lh-S_central",221,20,10,0],
+        [3146,"wm-lh-S_central_insula",21,220,20,0],
+        [3147,"wm-lh-S_cingulate-Main_part_and_Intracingulate",183,100,20,0],
+        [3148,"wm-lh-S_cingulate-Marginalis_part",221,20,100,0],
+        [3149,"wm-lh-S_circular_insula_anterior",221,60,140,0],
+        [3150,"wm-lh-S_circular_insula_inferior",221,20,220,0],
+        [3151,"wm-lh-S_circular_insula_superior",61,220,220,0],
+        [3152,"wm-lh-S_collateral_transverse_ant",100,200,200,0],
+        [3153,"wm-lh-S_collateral_transverse_post",10,200,200,0],
+        [3154,"wm-lh-S_frontal_inferior",221,220,20,0],
+        [3155,"wm-lh-S_frontal_middle",141,20,100,0],
+        [3156,"wm-lh-S_frontal_superior",61,220,100,0],
+        [3157,"wm-lh-S_frontomarginal",21,220,60,0],
+        [3158,"wm-lh-S_intermedius_primus-Jensen",141,60,20,0],
+        [3159,"wm-lh-S_intraparietal-and_Parietal_transverse",143,20,220,0],
+        [3160,"wm-lh-S_occipital_anterior",61,20,180,0],
+        [3161,"wm-lh-S_occipital_middle_and_Lunatus",101,60,220,0],
+        [3162,"wm-lh-S_occipital_superior_and_transversalis",21,20,140,0],
+        [3163,"wm-lh-S_occipito-temporal_lateral",221,140,20,0],
+        [3164,"wm-lh-S_occipito-temporal_medial_and_S_Lingual",141,100,220,0],
+        [3165,"wm-lh-S_orbital-H_shapped",101,20,20,0],
+        [3166,"wm-lh-S_orbital_lateral",221,100,20,0],
+        [3167,"wm-lh-S_orbital_medial-Or_olfactory",181,200,20,0],
+        [3168,"wm-lh-S_paracentral",21,180,140,0],
+        [3169,"wm-lh-S_parieto_occipital",101,100,180,0],
+        [3170,"wm-lh-S_pericallosal",181,220,20,0],
+        [3171,"wm-lh-S_postcentral",21,140,200,0],
+        [3172,"wm-lh-S_precentral-Inferior-part",21,20,240,0],
+        [3173,"wm-lh-S_precentral-Superior-part",21,20,200,0],
+        [3174,"wm-lh-S_subcentral_ant",61,180,60,0],
+        [3175,"wm-lh-S_subcentral_post",61,180,250,0],
+        [3176,"wm-lh-S_suborbital",21,20,60,0],
+        [3177,"wm-lh-S_subparietal",101,60,60,0],
+        [3178,"wm-lh-S_supracingulate",21,220,220,0],
+        [3179,"wm-lh-S_temporal_inferior",21,180,180,0],
+        [3180,"wm-lh-S_temporal_superior",223,220,60,0],
+        [3181,"wm-lh-S_temporal_transverse",221,60,60,0],
+        [4100,"wm-rh-Unknown",0,0,0,0],
+        [4101,"wm-rh-Corpus_callosum",50,50,50,0],
+        [4102,"wm-rh-G_and_S_Insula_ONLY_AVERAGE",180,20,30,0],
+        [4103,"wm-rh-G_cingulate-Isthmus",60,25,25,0],
+        [4104,"wm-rh-G_cingulate-Main_part",25,60,60,0],
+        [4105,"wm-rh-G_cuneus",180,20,20,0],
+        [4106,"wm-rh-G_frontal_inf-Opercular_part",220,20,100,0],
+        [4107,"wm-rh-G_frontal_inf-Orbital_part",140,60,60,0],
+        [4108,"wm-rh-G_frontal_inf-Triangular_part",180,220,140,0],
+        [4109,"wm-rh-G_frontal_middle",140,100,180,0],
+        [4110,"wm-rh-G_frontal_superior",180,20,140,0],
+        [4111,"wm-rh-G_frontomarginal",140,20,140,0],
+        [4112,"wm-rh-G_insular_long",21,10,10,0],
+        [4113,"wm-rh-G_insular_short",225,140,140,0],
+        [4114,"wm-rh-G_and_S_occipital_inferior",23,60,180,0],
+        [4115,"wm-rh-G_occipital_middle",180,60,180,0],
+        [4116,"wm-rh-G_occipital_superior",20,220,60,0],
+        [4117,"wm-rh-G_occipit-temp_lat-Or_fusiform",60,20,140,0],
+        [4118,"wm-rh-G_occipit-temp_med-Lingual_part",220,180,140,0],
+        [4119,"wm-rh-G_occipit-temp_med-Parahippocampal_part",65,100,20,0],
+        [4120,"wm-rh-G_orbital",220,60,20,0],
+        [4121,"wm-rh-G_paracentral",60,100,60,0],
+        [4122,"wm-rh-G_parietal_inferior-Angular_part",20,60,220,0],
+        [4123,"wm-rh-G_parietal_inferior-Supramarginal_part",100,100,60,0],
+        [4124,"wm-rh-G_parietal_superior",220,180,220,0],
+        [4125,"wm-rh-G_postcentral",20,180,140,0],
+        [4126,"wm-rh-G_precentral",60,140,180,0],
+        [4127,"wm-rh-G_precuneus",25,20,140,0],
+        [4128,"wm-rh-G_rectus",20,60,100,0],
+        [4129,"wm-rh-G_subcallosal",60,220,20,0],
+        [4130,"wm-rh-G_subcentral",60,20,220,0],
+        [4131,"wm-rh-G_temporal_inferior",220,220,100,0],
+        [4132,"wm-rh-G_temporal_middle",180,60,60,0],
+        [4133,"wm-rh-G_temp_sup-G_temp_transv_and_interm_S",60,60,220,0],
+        [4134,"wm-rh-G_temp_sup-Lateral_aspect",220,60,220,0],
+        [4135,"wm-rh-G_temp_sup-Planum_polare",65,220,60,0],
+        [4136,"wm-rh-G_temp_sup-Planum_tempolare",25,140,20,0],
+        [4137,"wm-rh-G_and_S_transverse_frontopolar",13,0,250,0],
+        [4138,"wm-rh-Lat_Fissure-ant_sgt-ramus_horizontal",61,20,220,0],
+        [4139,"wm-rh-Lat_Fissure-ant_sgt-ramus_vertical",61,20,60,0],
+        [4140,"wm-rh-Lat_Fissure-post_sgt",61,60,100,0],
+        [4141,"wm-rh-Medial_wall",25,25,25,0],
+        [4142,"wm-rh-Pole_occipital",140,20,60,0],
+        [4143,"wm-rh-Pole_temporal",220,180,20,0],
+        [4144,"wm-rh-S_calcarine",63,180,180,0],
+        [4145,"wm-rh-S_central",221,20,10,0],
+        [4146,"wm-rh-S_central_insula",21,220,20,0],
+        [4147,"wm-rh-S_cingulate-Main_part_and_Intracingulate",183,100,20,0],
+        [4148,"wm-rh-S_cingulate-Marginalis_part",221,20,100,0],
+        [4149,"wm-rh-S_circular_insula_anterior",221,60,140,0],
+        [4150,"wm-rh-S_circular_insula_inferior",221,20,220,0],
+        [4151,"wm-rh-S_circular_insula_superior",61,220,220,0],
+        [4152,"wm-rh-S_collateral_transverse_ant",100,200,200,0],
+        [4153,"wm-rh-S_collateral_transverse_post",10,200,200,0],
+        [4154,"wm-rh-S_frontal_inferior",221,220,20,0],
+        [4155,"wm-rh-S_frontal_middle",141,20,100,0],
+        [4156,"wm-rh-S_frontal_superior",61,220,100,0],
+        [4157,"wm-rh-S_frontomarginal",21,220,60,0],
+        [4158,"wm-rh-S_intermedius_primus-Jensen",141,60,20,0],
+        [4159,"wm-rh-S_intraparietal-and_Parietal_transverse",143,20,220,0],
+        [4160,"wm-rh-S_occipital_anterior",61,20,180,0],
+        [4161,"wm-rh-S_occipital_middle_and_Lunatus",101,60,220,0],
+        [4162,"wm-rh-S_occipital_superior_and_transversalis",21,20,140,0],
+        [4163,"wm-rh-S_occipito-temporal_lateral",221,140,20,0],
+        [4164,"wm-rh-S_occipito-temporal_medial_and_S_Lingual",141,100,220,0],
+        [4165,"wm-rh-S_orbital-H_shapped",101,20,20,0],
+        [4166,"wm-rh-S_orbital_lateral",221,100,20,0],
+        [4167,"wm-rh-S_orbital_medial-Or_olfactory",181,200,20,0],
+        [4168,"wm-rh-S_paracentral",21,180,140,0],
+        [4169,"wm-rh-S_parieto_occipital",101,100,180,0],
+        [4170,"wm-rh-S_pericallosal",181,220,20,0],
+        [4171,"wm-rh-S_postcentral",21,140,200,0],
+        [4172,"wm-rh-S_precentral-Inferior-part",21,20,240,0],
+        [4173,"wm-rh-S_precentral-Superior-part",21,20,200,0],
+        [4174,"wm-rh-S_subcentral_ant",61,180,60,0],
+        [4175,"wm-rh-S_subcentral_post",61,180,250,0],
+        [4176,"wm-rh-S_suborbital",21,20,60,0],
+        [4177,"wm-rh-S_subparietal",101,60,60,0],
+        [4178,"wm-rh-S_supracingulate",21,220,220,0],
+        [4179,"wm-rh-S_temporal_inferior",21,180,180,0],
+        [4180,"wm-rh-S_temporal_superior",223,220,60,0],
+        [4181,"wm-rh-S_temporal_transverse",221,60,60,0],
+        [5001,"Left-UnsegmentedWhiteMatter",20,30,40,0],
+        [5002,"Right-UnsegmentedWhiteMatter",20,30,40,0],
+        [5100,"fmajor",204,102,102,0],
+        [5101,"fminor",204,102,102,0],
+        [5102,"cc.body",204,102,102,0],
+        [5103,"cc.bodyc",255,153,153,0],
+        [5104,"cc.bodypf",255,153,153,0],
+        [5105,"cc.bodypm",255,204,204,0],
+        [5106,"cc.bodyp",255,102,102,0],
+        [5107,"cc.bodyt",255,204,204,0],
+        [5108,"cc.genu",255,102,102,0],
+        [5109,"cc.rostrum",204,51,51,0],
+        [5110,"cc.splenium",204,51,51,0],
+        [5111,"acomm",204,102,102,0],
+        [5112,"mcp",102,51,153,0],
+        [5200,"lh.atr",255,255,102,0],
+        [5201,"lh.cab",153,204,0,0],
+        [5202,"lh.ccg",0,153,153,0],
+        [5203,"lh.cst",204,153,255,0],
+        [5204,"lh.ilf",255,153,51,0],
+        [5205,"lh.slfp",204,204,204,0],
+        [5206,"lh.slft",153,255,255,0],
+        [5207,"lh.unc",102,153,255,0],
+        [5208,"lh.cb",0,153,153,0],
+        [5209,"lh.slf",204,204,204,0],
+        [5210,"lh.af",153,255,255,0],
+        [5211,"lh.ifof",51,153,51,0],
+        [5212,"lh.fx",255,153,204,0],
+        [5213,"lh.fat",204,51,102,0],
+        [5214,"lh.or",153,102,255,0],
+        [5215,"lh.mlf",255,255,204,0],
+        [5216,"lh.slf1",51,204,255,0],
+        [5217,"lh.slf2",51,255,204,0],
+        [5218,"lh.slf3",204,204,204,0],
+        [5219,"lh.afd",153,255,255,0],
+        [5220,"lh.afv",153,255,255,0],
+        [5221,"lh.ar",153,0,204,0],
+        [5222,"lh.cbd",0,153,153,0],
+        [5223,"lh.cbv",153,204,0,0],
+        [5224,"lh.emc",51,153,51,0],
+        [5225,"lh.uf",102,153,255,0],
+        [5300,"rh.atr",255,255,102,0],
+        [5301,"rh.cab",153,204,0,0],
+        [5302,"rh.ccg",0,153,153,0],
+        [5303,"rh.cst",204,153,255,0],
+        [5304,"rh.ilf",255,153,51,0],
+        [5305,"rh.slfp",204,204,204,0],
+        [5306,"rh.slft",153,255,255,0],
+        [5307,"rh.unc",102,153,255,0],
+        [5308,"rh.cb",0,153,153,0],
+        [5309,"rh.slf",204,204,204,0],
+        [5310,"rh.af",153,255,255,0],
+        [5311,"rh.ifof",51,153,51,0],
+        [5312,"rh.fx",255,153,204,0],
+        [5313,"rh.fat",204,51,102,0],
+        [5314,"rh.or",153,102,255,0],
+        [5315,"rh.mlf",255,255,204,0],
+        [5316,"rh.slf1",51,204,255,0],
+        [5317,"rh.slf2",51,255,204,0],
+        [5318,"rh.slf3",204,204,204,0],
+        [5319,"rh.afd",153,255,255,0],
+        [5320,"rh.afv",153,255,255,0],
+        [5321,"rh.ar",153,0,204,0],
+        [5322,"rh.cbd",0,153,153,0],
+        [5323,"rh.cbv",153,204,0,0],
+        [5324,"rh.emc",51,153,51,0],
+        [5325,"rh.uf",102,153,255,0],
+        [6000,"CST-orig",0,255,0,0],
+        [6001,"CST-hammer",255,255,0,0],
+        [6002,"CST-CVS",0,255,255,0],
+        [6003,"CST-flirt",0,0,255,0],
+        [6010,"Left-SLF1",236,16,231,0],
+        [6020,"Right-SLF1",237,18,232,0],
+        [6030,"Left-SLF3",236,13,227,0],
+        [6040,"Right-SLF3",236,17,228,0],
+        [6050,"Left-CST",1,255,1,0],
+        [6060,"Right-CST",2,255,1,0],
+        [6070,"Left-SLF2",236,14,230,0],
+        [6080,"Right-SLF2",237,14,230,0],
+        [7001,"Lateral-nucleus",72,132,181,0],
+        [7002,"Basolateral-nucleus",243,243,243,0],
+        [7003,"Basal-nucleus",207,63,79,0],
+        [7004,"Centromedial-nucleus",121,20,135,0],
+        [7005,"Central-nucleus",197,60,248,0],
+        [7006,"Medial-nucleus",2,149,2,0],
+        [7007,"Cortical-nucleus",221,249,166,0],
+        [7008,"Accessory-Basal-nucleus",232,146,35,0],
+        [7009,"Corticoamygdaloid-transitio",20,60,120,0],
+        [7010,"Anterior-amygdaloid-area-AAA",250,250,0,0],
+        [7011,"Fusion-amygdala-HP-FAH",122,187,222,0],
+        [7012,"Hippocampal-amygdala-transition-HATA",237,12,177,0],
+        [7013,"Endopiriform-nucleus",10,49,255,0],
+        [7014,"Lateral-nucleus-olfactory-tract",205,184,144,0],
+        [7015,"Paralaminar-nucleus",45,205,165,0],
+        [7016,"Intercalated-nucleus",117,160,175,0],
+        [7017,"Prepiriform-cortex",221,217,21,0],
+        [7018,"Periamygdaloid-cortex",20,60,120,0],
+        [7019,"Envelope-Amygdala",141,21,100,0],
+        [7020,"Extranuclear-Amydala",225,140,141,0],
+        [7100,"Brainstem-inferior-colliculus",42,201,168,0],
+        [7101,"Brainstem-cochlear-nucleus",168,104,162,0],
+        [7201,"DR",121,255,250,0],
+        [7202,"MnR",0,255,0,0],
+        [7203,"PAG",153,153,255,0],
+        [7204,"VTA",255,0,255,0],
+        [7301,"Left-LC",0,0,255,0],
+        [7302,"Left-LDTg",255,127,0,0],
+        [7303,"Left-mRt",255,0,0,0],
+        [7304,"Left-PBC",255,255,0,0],
+        [7305,"Left-PnO",0,127,255,0],
+        [7306,"Left-PTg",127,0,255,0],
+        [7401,"Right-LC",0,0,255,0],
+        [7402,"Right-LDTg",255,127,0,0],
+        [7403,"Right-mRt",255,0,0,0],
+        [7404,"Right-PBC",255,255,0,0],
+        [7405,"Right-PnO",0,127,255,0],
+        [7406,"Right-PTg",127,0,255,0],
+        [8001,"Thalamus-Anterior",74,130,181,0],
+        [8002,"Thalamus-Ventral-anterior",242,241,240,0],
+        [8003,"Thalamus-Lateral-dorsal",206,65,78,0],
+        [8004,"Thalamus-Lateral-posterior",120,21,133,0],
+        [8005,"Thalamus-Ventral-lateral",195,61,246,0],
+        [8006,"Thalamus-Ventral-posterior-medial",3,147,6,0],
+        [8007,"Thalamus-Ventral-posterior-lateral",220,251,163,0],
+        [8008,"Thalamus-intralaminar",232,146,33,0],
+        [8009,"Thalamus-centromedian",4,114,14,0],
+        [8010,"Thalamus-mediodorsal",121,184,220,0],
+        [8011,"Thalamus-medial",235,11,175,0],
+        [8012,"Thalamus-pulvinar",12,46,250,0],
+        [8013,"Thalamus-lateral-geniculate",203,182,143,0],
+        [8014,"Thalamus-medial-geniculate",42,204,167,0],
+        [8103,"Left-AV",0,85,0,0],
+        [8104,"Left-CeM",170,85,0,0],
+        [8105,"Left-CL",0,170,0,0],
+        [8106,"Left-CM",170,170,0,0],
+        [8108,"Left-LD",170,255,0,0],
+        [8109,"Left-LGN",0,0,127,0],
+        [8110,"Left-LP",0,85,127,0],
+        [8111,"Left-L-Sg",170,85,127,0],
+        [8112,"Left-MDl",0,170,127,0],
+        [8113,"Left-MDm",170,170,127,0],
+        [8115,"Left-MGN",170,255,127,0],
+        [8116,"Left-MV(Re)",0,0,255,0],
+        [8117,"Left-Pc",170,0,255,0],
+        [8118,"Left-Pf",0,85,255,0],
+        [8119,"Left-Pt",170,85,255,0],
+        [8120,"Left-PuA",0,170,255,0],
+        [8121,"Left-PuI",170,170,255,0],
+        [8122,"Left-PuL",0,255,255,0],
+        [8123,"Left-PuM",170,255,255,0],
+        [8125,"Left-R",255,0,0,0],
+        [8126,"Left-VA",85,85,0,0],
+        [8127,"Left-VAmc",255,85,0,0],
+        [8128,"Left-VLa",85,170,0,0],
+        [8129,"Left-VLp",255,170,0,0],
+        [8130,"Left-VM",85,255,0,0],
+        [8133,"Left-VPL",255,0,255,0],
+        [8134,"Left-PaV",120,18,134,0],
+        [8203,"Right-AV",0,85,0,0],
+        [8204,"Right-CeM",170,85,0,0],
+        [8205,"Right-CL",0,170,0,0],
+        [8206,"Right-CM",170,170,0,0],
+        [8208,"Right-LD",170,255,0,0],
+        [8209,"Right-LGN",0,0,127,0],
+        [8210,"Right-LP",0,85,127,0],
+        [8211,"Right-L-Sg",170,85,127,0],
+        [8212,"Right-MDl",0,170,127,0],
+        [8213,"Right-MDm",170,170,127,0],
+        [8215,"Right-MGN",170,255,127,0],
+        [8216,"Right-MV(Re)",0,0,255,0],
+        [8217,"Right-Pc",170,0,255,0],
+        [8218,"Right-Pf",0,85,255,0],
+        [8219,"Right-Pt",170,85,255,0],
+        [8220,"Right-PuA",0,170,255,0],
+        [8221,"Right-PuI",170,170,255,0],
+        [8222,"Right-PuL",0,255,255,0],
+        [8223,"Right-PuM",170,255,255,0],
+        [8225,"Right-R",255,0,0,0],
+        [8226,"Right-VA",85,85,0,0],
+        [8227,"Right-VAmc",255,85,0,0],
+        [8228,"Right-VLa",85,170,0,0],
+        [8229,"Right-VLp",255,170,0,0],
+        [8230,"Right-VM",85,255,0,0],
+        [8233,"Right-VPL",255,0,255,0],
+        [8234,"Right-PaV",120,18,134,0],
+        [9000,"ctx-lh-prefrontal",50,100,30,0],
+        [9001,"ctx-lh-primary-motor",30,100,45,0],
+        [9002,"ctx-lh-premotor",130,100,165,0],
+        [9003,"ctx-lh-temporal",105,25,5,0],
+        [9004,"ctx-lh-posterior-parietal",125,70,55,0],
+        [9005,"ctx-lh-prim-sec-somatosensory",225,20,105,0],
+        [9006,"ctx-lh-occipital",225,20,15,0],
+        [9500,"ctx-rh-prefrontal",50,200,30,0],
+        [9501,"ctx-rh-primary-motor",30,150,45,0],
+        [9502,"ctx-rh-premotor",130,150,165,0],
+        [9503,"ctx-rh-temporal",105,75,5,0],
+        [9504,"ctx-rh-posterior-parietal",125,120,55,0],
+        [9505,"ctx-rh-prim-sec-somatosensory",225,70,105,0],
+        [9506,"ctx-rh-occipital",225,70,15,0],
+        [11100,"ctx_lh_Unknown",0,0,0,0],
+        [11101,"ctx_lh_G_and_S_frontomargin",23,220,60,0],
+        [11102,"ctx_lh_G_and_S_occipital_inf",23,60,180,0],
+        [11103,"ctx_lh_G_and_S_paracentral",63,100,60,0],
+        [11104,"ctx_lh_G_and_S_subcentral",63,20,220,0],
+        [11105,"ctx_lh_G_and_S_transv_frontopol",13,0,250,0],
+        [11106,"ctx_lh_G_and_S_cingul-Ant",26,60,0,0],
+        [11107,"ctx_lh_G_and_S_cingul-Mid-Ant",26,60,75,0],
+        [11108,"ctx_lh_G_and_S_cingul-Mid-Post",26,60,150,0],
+        [11109,"ctx_lh_G_cingul-Post-dorsal",25,60,250,0],
+        [11110,"ctx_lh_G_cingul-Post-ventral",60,25,25,0],
+        [11111,"ctx_lh_G_cuneus",180,20,20,0],
+        [11112,"ctx_lh_G_front_inf-Opercular",220,20,100,0],
+        [11113,"ctx_lh_G_front_inf-Orbital",140,60,60,0],
+        [11114,"ctx_lh_G_front_inf-Triangul",180,220,140,0],
+        [11115,"ctx_lh_G_front_middle",140,100,180,0],
+        [11116,"ctx_lh_G_front_sup",180,20,140,0],
+        [11117,"ctx_lh_G_Ins_lg_and_S_cent_ins",23,10,10,0],
+        [11118,"ctx_lh_G_insular_short",225,140,140,0],
+        [11119,"ctx_lh_G_occipital_middle",180,60,180,0],
+        [11120,"ctx_lh_G_occipital_sup",20,220,60,0],
+        [11121,"ctx_lh_G_oc-temp_lat-fusifor",60,20,140,0],
+        [11122,"ctx_lh_G_oc-temp_med-Lingual",220,180,140,0],
+        [11123,"ctx_lh_G_oc-temp_med-Parahip",65,100,20,0],
+        [11124,"ctx_lh_G_orbital",220,60,20,0],
+        [11125,"ctx_lh_G_pariet_inf-Angular",20,60,220,0],
+        [11126,"ctx_lh_G_pariet_inf-Supramar",100,100,60,0],
+        [11127,"ctx_lh_G_parietal_sup",220,180,220,0],
+        [11128,"ctx_lh_G_postcentral",20,180,140,0],
+        [11129,"ctx_lh_G_precentral",60,140,180,0],
+        [11130,"ctx_lh_G_precuneus",25,20,140,0],
+        [11131,"ctx_lh_G_rectus",20,60,100,0],
+        [11132,"ctx_lh_G_subcallosal",60,220,20,0],
+        [11133,"ctx_lh_G_temp_sup-G_T_transv",60,60,220,0],
+        [11134,"ctx_lh_G_temp_sup-Lateral",220,60,220,0],
+        [11135,"ctx_lh_G_temp_sup-Plan_polar",65,220,60,0],
+        [11136,"ctx_lh_G_temp_sup-Plan_tempo",25,140,20,0],
+        [11137,"ctx_lh_G_temporal_inf",220,220,100,0],
+        [11138,"ctx_lh_G_temporal_middle",180,60,60,0],
+        [11139,"ctx_lh_Lat_Fis-ant-Horizont",61,20,220,0],
+        [11140,"ctx_lh_Lat_Fis-ant-Vertical",61,20,60,0],
+        [11141,"ctx_lh_Lat_Fis-post",61,60,100,0],
+        [11142,"ctx_lh_Medial_wall",25,25,25,0],
+        [11143,"ctx_lh_Pole_occipital",140,20,60,0],
+        [11144,"ctx_lh_Pole_temporal",220,180,20,0],
+        [11145,"ctx_lh_S_calcarine",63,180,180,0],
+        [11146,"ctx_lh_S_central",221,20,10,0],
+        [11147,"ctx_lh_S_cingul-Marginalis",221,20,100,0],
+        [11148,"ctx_lh_S_circular_insula_ant",221,60,140,0],
+        [11149,"ctx_lh_S_circular_insula_inf",221,20,220,0],
+        [11150,"ctx_lh_S_circular_insula_sup",61,220,220,0],
+        [11151,"ctx_lh_S_collat_transv_ant",100,200,200,0],
+        [11152,"ctx_lh_S_collat_transv_post",10,200,200,0],
+        [11153,"ctx_lh_S_front_inf",221,220,20,0],
+        [11154,"ctx_lh_S_front_middle",141,20,100,0],
+        [11155,"ctx_lh_S_front_sup",61,220,100,0],
+        [11156,"ctx_lh_S_interm_prim-Jensen",141,60,20,0],
+        [11157,"ctx_lh_S_intrapariet_and_P_trans",143,20,220,0],
+        [11158,"ctx_lh_S_oc_middle_and_Lunatus",101,60,220,0],
+        [11159,"ctx_lh_S_oc_sup_and_transversal",21,20,140,0],
+        [11160,"ctx_lh_S_occipital_ant",61,20,180,0],
+        [11161,"ctx_lh_S_oc-temp_lat",221,140,20,0],
+        [11162,"ctx_lh_S_oc-temp_med_and_Lingual",141,100,220,0],
+        [11163,"ctx_lh_S_orbital_lateral",221,100,20,0],
+        [11164,"ctx_lh_S_orbital_med-olfact",181,200,20,0],
+        [11165,"ctx_lh_S_orbital-H_Shaped",101,20,20,0],
+        [11166,"ctx_lh_S_parieto_occipital",101,100,180,0],
+        [11167,"ctx_lh_S_pericallosal",181,220,20,0],
+        [11168,"ctx_lh_S_postcentral",21,140,200,0],
+        [11169,"ctx_lh_S_precentral-inf-part",21,20,240,0],
+        [11170,"ctx_lh_S_precentral-sup-part",21,20,200,0],
+        [11171,"ctx_lh_S_suborbital",21,20,60,0],
+        [11172,"ctx_lh_S_subparietal",101,60,60,0],
+        [11173,"ctx_lh_S_temporal_inf",21,180,180,0],
+        [11174,"ctx_lh_S_temporal_sup",223,220,60,0],
+        [11175,"ctx_lh_S_temporal_transverse",221,60,60,0],
+        [12100,"ctx_rh_Unknown",0,0,0,0],
+        [12101,"ctx_rh_G_and_S_frontomargin",23,220,60,0],
+        [12102,"ctx_rh_G_and_S_occipital_inf",23,60,180,0],
+        [12103,"ctx_rh_G_and_S_paracentral",63,100,60,0],
+        [12104,"ctx_rh_G_and_S_subcentral",63,20,220,0],
+        [12105,"ctx_rh_G_and_S_transv_frontopol",13,0,250,0],
+        [12106,"ctx_rh_G_and_S_cingul-Ant",26,60,0,0],
+        [12107,"ctx_rh_G_and_S_cingul-Mid-Ant",26,60,75,0],
+        [12108,"ctx_rh_G_and_S_cingul-Mid-Post",26,60,150,0],
+        [12109,"ctx_rh_G_cingul-Post-dorsal",25,60,250,0],
+        [12110,"ctx_rh_G_cingul-Post-ventral",60,25,25,0],
+        [12111,"ctx_rh_G_cuneus",180,20,20,0],
+        [12112,"ctx_rh_G_front_inf-Opercular",220,20,100,0],
+        [12113,"ctx_rh_G_front_inf-Orbital",140,60,60,0],
+        [12114,"ctx_rh_G_front_inf-Triangul",180,220,140,0],
+        [12115,"ctx_rh_G_front_middle",140,100,180,0],
+        [12116,"ctx_rh_G_front_sup",180,20,140,0],
+        [12117,"ctx_rh_G_Ins_lg_and_S_cent_ins",23,10,10,0],
+        [12118,"ctx_rh_G_insular_short",225,140,140,0],
+        [12119,"ctx_rh_G_occipital_middle",180,60,180,0],
+        [12120,"ctx_rh_G_occipital_sup",20,220,60,0],
+        [12121,"ctx_rh_G_oc-temp_lat-fusifor",60,20,140,0],
+        [12122,"ctx_rh_G_oc-temp_med-Lingual",220,180,140,0],
+        [12123,"ctx_rh_G_oc-temp_med-Parahip",65,100,20,0],
+        [12124,"ctx_rh_G_orbital",220,60,20,0],
+        [12125,"ctx_rh_G_pariet_inf-Angular",20,60,220,0],
+        [12126,"ctx_rh_G_pariet_inf-Supramar",100,100,60,0],
+        [12127,"ctx_rh_G_parietal_sup",220,180,220,0],
+        [12128,"ctx_rh_G_postcentral",20,180,140,0],
+        [12129,"ctx_rh_G_precentral",60,140,180,0],
+        [12130,"ctx_rh_G_precuneus",25,20,140,0],
+        [12131,"ctx_rh_G_rectus",20,60,100,0],
+        [12132,"ctx_rh_G_subcallosal",60,220,20,0],
+        [12133,"ctx_rh_G_temp_sup-G_T_transv",60,60,220,0],
+        [12134,"ctx_rh_G_temp_sup-Lateral",220,60,220,0],
+        [12135,"ctx_rh_G_temp_sup-Plan_polar",65,220,60,0],
+        [12136,"ctx_rh_G_temp_sup-Plan_tempo",25,140,20,0],
+        [12137,"ctx_rh_G_temporal_inf",220,220,100,0],
+        [12138,"ctx_rh_G_temporal_middle",180,60,60,0],
+        [12139,"ctx_rh_Lat_Fis-ant-Horizont",61,20,220,0],
+        [12140,"ctx_rh_Lat_Fis-ant-Vertical",61,20,60,0],
+        [12141,"ctx_rh_Lat_Fis-post",61,60,100,0],
+        [12142,"ctx_rh_Medial_wall",25,25,25,0],
+        [12143,"ctx_rh_Pole_occipital",140,20,60,0],
+        [12144,"ctx_rh_Pole_temporal",220,180,20,0],
+        [12145,"ctx_rh_S_calcarine",63,180,180,0],
+        [12146,"ctx_rh_S_central",221,20,10,0],
+        [12147,"ctx_rh_S_cingul-Marginalis",221,20,100,0],
+        [12148,"ctx_rh_S_circular_insula_ant",221,60,140,0],
+        [12149,"ctx_rh_S_circular_insula_inf",221,20,220,0],
+        [12150,"ctx_rh_S_circular_insula_sup",61,220,220,0],
+        [12151,"ctx_rh_S_collat_transv_ant",100,200,200,0],
+        [12152,"ctx_rh_S_collat_transv_post",10,200,200,0],
+        [12153,"ctx_rh_S_front_inf",221,220,20,0],
+        [12154,"ctx_rh_S_front_middle",141,20,100,0],
+        [12155,"ctx_rh_S_front_sup",61,220,100,0],
+        [12156,"ctx_rh_S_interm_prim-Jensen",141,60,20,0],
+        [12157,"ctx_rh_S_intrapariet_and_P_trans",143,20,220,0],
+        [12158,"ctx_rh_S_oc_middle_and_Lunatus",101,60,220,0],
+        [12159,"ctx_rh_S_oc_sup_and_transversal",21,20,140,0],
+        [12160,"ctx_rh_S_occipital_ant",61,20,180,0],
+        [12161,"ctx_rh_S_oc-temp_lat",221,140,20,0],
+        [12162,"ctx_rh_S_oc-temp_med_and_Lingual",141,100,220,0],
+        [12163,"ctx_rh_S_orbital_lateral",221,100,20,0],
+        [12164,"ctx_rh_S_orbital_med-olfact",181,200,20,0],
+        [12165,"ctx_rh_S_orbital-H_Shaped",101,20,20,0],
+        [12166,"ctx_rh_S_parieto_occipital",101,100,180,0],
+        [12167,"ctx_rh_S_pericallosal",181,220,20,0],
+        [12168,"ctx_rh_S_postcentral",21,140,200,0],
+        [12169,"ctx_rh_S_precentral-inf-part",21,20,240,0],
+        [12170,"ctx_rh_S_precentral-sup-part",21,20,200,0],
+        [12171,"ctx_rh_S_suborbital",21,20,60,0],
+        [12172,"ctx_rh_S_subparietal",101,60,60,0],
+        [12173,"ctx_rh_S_temporal_inf",21,180,180,0],
+        [12174,"ctx_rh_S_temporal_sup",223,220,60,0],
+        [12175,"ctx_rh_S_temporal_transverse",221,60,60,0],
+        [13100,"wm_lh_Unknown",0,0,0,0],
+        [13101,"wm_lh_G_and_S_frontomargin",23,220,60,0],
+        [13102,"wm_lh_G_and_S_occipital_inf",23,60,180,0],
+        [13103,"wm_lh_G_and_S_paracentral",63,100,60,0],
+        [13104,"wm_lh_G_and_S_subcentral",63,20,220,0],
+        [13105,"wm_lh_G_and_S_transv_frontopol",13,0,250,0],
+        [13106,"wm_lh_G_and_S_cingul-Ant",26,60,0,0],
+        [13107,"wm_lh_G_and_S_cingul-Mid-Ant",26,60,75,0],
+        [13108,"wm_lh_G_and_S_cingul-Mid-Post",26,60,150,0],
+        [13109,"wm_lh_G_cingul-Post-dorsal",25,60,250,0],
+        [13110,"wm_lh_G_cingul-Post-ventral",60,25,25,0],
+        [13111,"wm_lh_G_cuneus",180,20,20,0],
+        [13112,"wm_lh_G_front_inf-Opercular",220,20,100,0],
+        [13113,"wm_lh_G_front_inf-Orbital",140,60,60,0],
+        [13114,"wm_lh_G_front_inf-Triangul",180,220,140,0],
+        [13115,"wm_lh_G_front_middle",140,100,180,0],
+        [13116,"wm_lh_G_front_sup",180,20,140,0],
+        [13117,"wm_lh_G_Ins_lg_and_S_cent_ins",23,10,10,0],
+        [13118,"wm_lh_G_insular_short",225,140,140,0],
+        [13119,"wm_lh_G_occipital_middle",180,60,180,0],
+        [13120,"wm_lh_G_occipital_sup",20,220,60,0],
+        [13121,"wm_lh_G_oc-temp_lat-fusifor",60,20,140,0],
+        [13122,"wm_lh_G_oc-temp_med-Lingual",220,180,140,0],
+        [13123,"wm_lh_G_oc-temp_med-Parahip",65,100,20,0],
+        [13124,"wm_lh_G_orbital",220,60,20,0],
+        [13125,"wm_lh_G_pariet_inf-Angular",20,60,220,0],
+        [13126,"wm_lh_G_pariet_inf-Supramar",100,100,60,0],
+        [13127,"wm_lh_G_parietal_sup",220,180,220,0],
+        [13128,"wm_lh_G_postcentral",20,180,140,0],
+        [13129,"wm_lh_G_precentral",60,140,180,0],
+        [13130,"wm_lh_G_precuneus",25,20,140,0],
+        [13131,"wm_lh_G_rectus",20,60,100,0],
+        [13132,"wm_lh_G_subcallosal",60,220,20,0],
+        [13133,"wm_lh_G_temp_sup-G_T_transv",60,60,220,0],
+        [13134,"wm_lh_G_temp_sup-Lateral",220,60,220,0],
+        [13135,"wm_lh_G_temp_sup-Plan_polar",65,220,60,0],
+        [13136,"wm_lh_G_temp_sup-Plan_tempo",25,140,20,0],
+        [13137,"wm_lh_G_temporal_inf",220,220,100,0],
+        [13138,"wm_lh_G_temporal_middle",180,60,60,0],
+        [13139,"wm_lh_Lat_Fis-ant-Horizont",61,20,220,0],
+        [13140,"wm_lh_Lat_Fis-ant-Vertical",61,20,60,0],
+        [13141,"wm_lh_Lat_Fis-post",61,60,100,0],
+        [13142,"wm_lh_Medial_wall",25,25,25,0],
+        [13143,"wm_lh_Pole_occipital",140,20,60,0],
+        [13144,"wm_lh_Pole_temporal",220,180,20,0],
+        [13145,"wm_lh_S_calcarine",63,180,180,0],
+        [13146,"wm_lh_S_central",221,20,10,0],
+        [13147,"wm_lh_S_cingul-Marginalis",221,20,100,0],
+        [13148,"wm_lh_S_circular_insula_ant",221,60,140,0],
+        [13149,"wm_lh_S_circular_insula_inf",221,20,220,0],
+        [13150,"wm_lh_S_circular_insula_sup",61,220,220,0],
+        [13151,"wm_lh_S_collat_transv_ant",100,200,200,0],
+        [13152,"wm_lh_S_collat_transv_post",10,200,200,0],
+        [13153,"wm_lh_S_front_inf",221,220,20,0],
+        [13154,"wm_lh_S_front_middle",141,20,100,0],
+        [13155,"wm_lh_S_front_sup",61,220,100,0],
+        [13156,"wm_lh_S_interm_prim-Jensen",141,60,20,0],
+        [13157,"wm_lh_S_intrapariet_and_P_trans",143,20,220,0],
+        [13158,"wm_lh_S_oc_middle_and_Lunatus",101,60,220,0],
+        [13159,"wm_lh_S_oc_sup_and_transversal",21,20,140,0],
+        [13160,"wm_lh_S_occipital_ant",61,20,180,0],
+        [13161,"wm_lh_S_oc-temp_lat",221,140,20,0],
+        [13162,"wm_lh_S_oc-temp_med_and_Lingual",141,100,220,0],
+        [13163,"wm_lh_S_orbital_lateral",221,100,20,0],
+        [13164,"wm_lh_S_orbital_med-olfact",181,200,20,0],
+        [13165,"wm_lh_S_orbital-H_Shaped",101,20,20,0],
+        [13166,"wm_lh_S_parieto_occipital",101,100,180,0],
+        [13167,"wm_lh_S_pericallosal",181,220,20,0],
+        [13168,"wm_lh_S_postcentral",21,140,200,0],
+        [13169,"wm_lh_S_precentral-inf-part",21,20,240,0],
+        [13170,"wm_lh_S_precentral-sup-part",21,20,200,0],
+        [13171,"wm_lh_S_suborbital",21,20,60,0],
+        [13172,"wm_lh_S_subparietal",101,60,60,0],
+        [13173,"wm_lh_S_temporal_inf",21,180,180,0],
+        [13174,"wm_lh_S_temporal_sup",223,220,60,0],
+        [13175,"wm_lh_S_temporal_transverse",221,60,60,0],
+        [14100,"wm_rh_Unknown",0,0,0,0],
+        [14101,"wm_rh_G_and_S_frontomargin",23,220,60,0],
+        [14102,"wm_rh_G_and_S_occipital_inf",23,60,180,0],
+        [14103,"wm_rh_G_and_S_paracentral",63,100,60,0],
+        [14104,"wm_rh_G_and_S_subcentral",63,20,220,0],
+        [14105,"wm_rh_G_and_S_transv_frontopol",13,0,250,0],
+        [14106,"wm_rh_G_and_S_cingul-Ant",26,60,0,0],
+        [14107,"wm_rh_G_and_S_cingul-Mid-Ant",26,60,75,0],
+        [14108,"wm_rh_G_and_S_cingul-Mid-Post",26,60,150,0],
+        [14109,"wm_rh_G_cingul-Post-dorsal",25,60,250,0],
+        [14110,"wm_rh_G_cingul-Post-ventral",60,25,25,0],
+        [14111,"wm_rh_G_cuneus",180,20,20,0],
+        [14112,"wm_rh_G_front_inf-Opercular",220,20,100,0],
+        [14113,"wm_rh_G_front_inf-Orbital",140,60,60,0],
+        [14114,"wm_rh_G_front_inf-Triangul",180,220,140,0],
+        [14115,"wm_rh_G_front_middle",140,100,180,0],
+        [14116,"wm_rh_G_front_sup",180,20,140,0],
+        [14117,"wm_rh_G_Ins_lg_and_S_cent_ins",23,10,10,0],
+        [14118,"wm_rh_G_insular_short",225,140,140,0],
+        [14119,"wm_rh_G_occipital_middle",180,60,180,0],
+        [14120,"wm_rh_G_occipital_sup",20,220,60,0],
+        [14121,"wm_rh_G_oc-temp_lat-fusifor",60,20,140,0],
+        [14122,"wm_rh_G_oc-temp_med-Lingual",220,180,140,0],
+        [14123,"wm_rh_G_oc-temp_med-Parahip",65,100,20,0],
+        [14124,"wm_rh_G_orbital",220,60,20,0],
+        [14125,"wm_rh_G_pariet_inf-Angular",20,60,220,0],
+        [14126,"wm_rh_G_pariet_inf-Supramar",100,100,60,0],
+        [14127,"wm_rh_G_parietal_sup",220,180,220,0],
+        [14128,"wm_rh_G_postcentral",20,180,140,0],
+        [14129,"wm_rh_G_precentral",60,140,180,0],
+        [14130,"wm_rh_G_precuneus",25,20,140,0],
+        [14131,"wm_rh_G_rectus",20,60,100,0],
+        [14132,"wm_rh_G_subcallosal",60,220,20,0],
+        [14133,"wm_rh_G_temp_sup-G_T_transv",60,60,220,0],
+        [14134,"wm_rh_G_temp_sup-Lateral",220,60,220,0],
+        [14135,"wm_rh_G_temp_sup-Plan_polar",65,220,60,0],
+        [14136,"wm_rh_G_temp_sup-Plan_tempo",25,140,20,0],
+        [14137,"wm_rh_G_temporal_inf",220,220,100,0],
+        [14138,"wm_rh_G_temporal_middle",180,60,60,0],
+        [14139,"wm_rh_Lat_Fis-ant-Horizont",61,20,220,0],
+        [14140,"wm_rh_Lat_Fis-ant-Vertical",61,20,60,0],
+        [14141,"wm_rh_Lat_Fis-post",61,60,100,0],
+        [14142,"wm_rh_Medial_wall",25,25,25,0],
+        [14143,"wm_rh_Pole_occipital",140,20,60,0],
+        [14144,"wm_rh_Pole_temporal",220,180,20,0],
+        [14145,"wm_rh_S_calcarine",63,180,180,0],
+        [14146,"wm_rh_S_central",221,20,10,0],
+        [14147,"wm_rh_S_cingul-Marginalis",221,20,100,0],
+        [14148,"wm_rh_S_circular_insula_ant",221,60,140,0],
+        [14149,"wm_rh_S_circular_insula_inf",221,20,220,0],
+        [14150,"wm_rh_S_circular_insula_sup",61,220,220,0],
+        [14151,"wm_rh_S_collat_transv_ant",100,200,200,0],
+        [14152,"wm_rh_S_collat_transv_post",10,200,200,0],
+        [14153,"wm_rh_S_front_inf",221,220,20,0],
+        [14154,"wm_rh_S_front_middle",141,20,100,0],
+        [14155,"wm_rh_S_front_sup",61,220,100,0],
+        [14156,"wm_rh_S_interm_prim-Jensen",141,60,20,0],
+        [14157,"wm_rh_S_intrapariet_and_P_trans",143,20,220,0],
+        [14158,"wm_rh_S_oc_middle_and_Lunatus",101,60,220,0],
+        [14159,"wm_rh_S_oc_sup_and_transversal",21,20,140,0],
+        [14160,"wm_rh_S_occipital_ant",61,20,180,0],
+        [14161,"wm_rh_S_oc-temp_lat",221,140,20,0],
+        [14162,"wm_rh_S_oc-temp_med_and_Lingual",141,100,220,0],
+        [14163,"wm_rh_S_orbital_lateral",221,100,20,0],
+        [14164,"wm_rh_S_orbital_med-olfact",181,200,20,0],
+        [14165,"wm_rh_S_orbital-H_Shaped",101,20,20,0],
+        [14166,"wm_rh_S_parieto_occipital",101,100,180,0],
+        [14167,"wm_rh_S_pericallosal",181,220,20,0],
+        [14168,"wm_rh_S_postcentral",21,140,200,0],
+        [14169,"wm_rh_S_precentral-inf-part",21,20,240,0],
+        [14170,"wm_rh_S_precentral-sup-part",21,20,200,0],
+        [14171,"wm_rh_S_suborbital",21,20,60,0],
+        [14172,"wm_rh_S_subparietal",101,60,60,0],
+        [14173,"wm_rh_S_temporal_inf",21,180,180,0],
+        [14174,"wm_rh_S_temporal_sup",223,220,60,0],
+        [14175,"wm_rh_S_temporal_transverse",221,60,60,0]),
+        dtype="object")
\ No newline at end of file
diff --git a/fsqc/outlierDetection.py b/fsqc/outlierDetection.py
new file mode 100644
index 0000000..5e910f5
--- /dev/null
+++ b/fsqc/outlierDetection.py
@@ -0,0 +1,906 @@
+"""
+This module provides a function to evaluate potential outliers in the
+aseg.stats, aparc.stats, and hypothalamic and hippocampal values (if present).
+
+"""
+
+# ------------------------------------------------------------------------------
+# subfunctions
+
+
+def readAsegStats(path_aseg_stats):
+    """
+    A function to read aseg.stats files.
+
+    """
+
+    # read file
+    with open(path_aseg_stats) as stats_file:
+        aseg_stats = stats_file.read().splitlines()
+
+    # initialize
+    aseg = dict()
+
+    # read measures
+    for line in aseg_stats:
+        if "# Measure BrainSeg," in line:
+            aseg.update({"aseg.BrainSeg": float(line.split(",")[3])})
+        elif "# Measure BrainSegNotVent," in line:
+            aseg.update({"aseg.BrainSegNotVent": float(line.split(",")[3])})
+        elif "# Measure BrainSegNotVentSurf," in line:
+            aseg.update({"aseg.BrainSegNotVentSurf": float(line.split(",")[3])})
+        elif "# Measure VentricleChoroidVol," in line:
+            aseg.update({"aseg.VentricleChoroidVol": float(line.split(",")[3])})
+        elif "# Measure lhCortex," in line:
+            aseg.update({"aseg.lhCortex": float(line.split(",")[3])})
+        elif "# Measure rhCortex," in line:
+            aseg.update({"aseg.rhCortex": float(line.split(",")[3])})
+        elif "# Measure Cortex," in line:
+            aseg.update({"aseg.Cortex": float(line.split(",")[3])})
+        elif "# Measure lhCerebralWhiteMatter," in line:
+            aseg.update({"aseg.lhCerebralWhiteMatter": float(line.split(",")[3])})
+        elif "# Measure rhCerebralWhiteMatter," in line:
+            aseg.update({"aseg.rhCerebralWhiteMatter": float(line.split(",")[3])})
+        elif "# Measure CerebralWhiteMatter," in line:
+            aseg.update({"aseg.CerebralWhiteMatter": float(line.split(",")[3])})
+        elif "# Measure SubCortGray," in line:
+            aseg.update({"aseg.SubCortGray": float(line.split(",")[3])})
+        elif "# Measure TotalGray," in line:
+            aseg.update({"aseg.TotalGray": float(line.split(",")[3])})
+        elif "# Measure SupraTentorial," in line:
+            aseg.update({"aseg.SupraTentorial": float(line.split(",")[3])})
+        elif "# Measure SupraTentorialNotVent," in line:
+            aseg.update({"aseg.SupraTentorialNotVent": float(line.split(",")[3])})
+        elif "# Measure SupraTentorialNotVentVox," in line:
+            aseg.update({"aseg.SupraTentorialNotVentVox": float(line.split(",")[3])})
+        elif "# Measure Mask," in line:
+            aseg.update({"aseg.Mask": float(line.split(",")[3])})
+        elif "# Measure BrainSegVol-to-eTIV," in line:
+            aseg.update({"aseg.BrainSegVol_to_eTIV": float(line.split(",")[3])})
+        elif "# Measure MaskVol-to-eTIV," in line:
+            aseg.update({"aseg.MaskVol_to_eTIV": float(line.split(",")[3])})
+        elif "# Measure lhSurfaceHoles," in line:
+            aseg.update({"aseg.lhSurfaceHoles": float(line.split(",")[3])})
+        elif "# Measure rhSurfaceHoles," in line:
+            aseg.update({"aseg.rhSurfaceHoles": float(line.split(",")[3])})
+        elif "# Measure SurfaceHoles," in line:
+            aseg.update({"aseg.SurfaceHoles": float(line.split(",")[3])})
+        elif "# Measure EstimatedTotalIntraCranialVol," in line:
+            aseg.update({"aseg.EstimatedTotalIntraCranialVol": float(line.split(",")[3])})
+        elif "Left-Lateral-Ventricle" in line:
+            aseg.update({"aseg.Left-Lateral-Ventricle": float(line.split()[3])})
+        elif "Left-Inf-Lat-Vent" in line:
+            aseg.update({"aseg.Left-Inf-Lat-Vent": float(line.split()[3])})
+        elif "Left-Cerebellum-White-Matter" in line:
+            aseg.update({"aseg.Left-Cerebellum-White-Matter": float(line.split()[3])})
+        elif "Left-Cerebellum-Cortex" in line:
+            aseg.update({"aseg.Left-Cerebellum-Cortex": float(line.split()[3])})
+        elif "Left-Thalamus-Proper" in line:
+            aseg.update({"aseg.Left-Thalamus-Proper": float(line.split()[3])})
+        elif "Left-Caudate" in line:
+            aseg.update({"aseg.Left-Caudate": float(line.split()[3])})
+        elif "Left-Putamen" in line:
+            aseg.update({"aseg.Left-Putamen": float(line.split()[3])})
+        elif "Left-Pallidum" in line:
+            aseg.update({"aseg.Left-Pallidum": float(line.split()[3])})
+        elif "3rd-Ventricle" in line:
+            aseg.update({"aseg.3rd-Ventricle": float(line.split()[3])})
+        elif "4th-Ventricle" in line:
+            aseg.update({"aseg.4th-Ventricle": float(line.split()[3])})
+        elif "Brain-Stem" in line:
+            aseg.update({"aseg.Brain-Stem": float(line.split()[3])})
+        elif "Left-Hippocampus" in line:
+            aseg.update({"aseg.Left-Hippocampus": float(line.split()[3])})
+        elif "Left-Amygdala" in line:
+            aseg.update({"aseg.Left-Amygdala": float(line.split()[3])})
+        elif "CSF" in line:
+            aseg.update({"aseg.CSF": float(line.split()[3])})
+        elif "Left-Accumbens-area" in line:
+            aseg.update({"aseg.Left-Accumbens-area": float(line.split()[3])})
+        elif "Left-VentralDC" in line:
+            aseg.update({"aseg.Left-VentralDC": float(line.split()[3])})
+        elif "Left-vessel" in line:
+            aseg.update({"aseg.Left-vessel": float(line.split()[3])})
+        elif "Left-choroid-plexus" in line:
+            aseg.update({"aseg.Left-choroid-plexus": float(line.split()[3])})
+        elif "Right-Lateral-Ventricle" in line:
+            aseg.update({"aseg.Right-Lateral-Ventricle": float(line.split()[3])})
+        elif "Right-Inf-Lat-Vent" in line:
+            aseg.update({"aseg.Right-Inf-Lat-Vent": float(line.split()[3])})
+        elif "Right-Cerebellum-White-Matter" in line:
+            aseg.update({"aseg.Right-Cerebellum-White-Matter": float(line.split()[3])})
+        elif "Right-Cerebellum-Cortex" in line:
+            aseg.update({"aseg.Right-Cerebellum-Cortex": float(line.split()[3])})
+        elif "Right-Thalamus-Proper" in line:
+            aseg.update({"aseg.Right-Thalamus-Proper": float(line.split()[3])})
+        elif "Right-Caudate" in line:
+            aseg.update({"aseg.Right-Caudate": float(line.split()[3])})
+        elif "Right-Putamen" in line:
+            aseg.update({"aseg.Right-Putamen": float(line.split()[3])})
+        elif "Right-Pallidum" in line:
+            aseg.update({"aseg.Right-Pallidum": float(line.split()[3])})
+        elif "Right-Hippocampus" in line:
+            aseg.update({"aseg.Right-Hippocampus": float(line.split()[3])})
+        elif "Right-Amygdala" in line:
+            aseg.update({"aseg.Right-Amygdala": float(line.split()[3])})
+        elif "Right-Accumbens-area" in line:
+            aseg.update({"aseg.Right-Accumbens-area": float(line.split()[3])})
+        elif "Right-VentralDC" in line:
+            aseg.update({"aseg.Right-VentralDC": float(line.split()[3])})
+        elif "Right-vessel" in line:
+            aseg.update({"aseg.Right-vessel": float(line.split()[3])})
+        elif "Right-choroid-plexus" in line:
+            aseg.update({"aseg.Right-choroid-plexus": float(line.split()[3])})
+        elif "5th-Ventricle" in line:
+            aseg.update({"aseg.5th-Ventricle": float(line.split()[3])})
+        elif "WM-hypointensities" in line:
+            aseg.update({"aseg.WM-hypointensities": float(line.split()[3])})
+        elif "Left-WM-hypointensities" in line:
+            aseg.update({"aseg.Left-WM-hypointensities": float(line.split()[3])})
+        elif "Right-WM-hypointensities" in line:
+            aseg.update({"aseg.Right-WM-hypointensities": float(line.split()[3])})
+        elif "non-WM-hypointensities" in line:
+            aseg.update({"aseg.non-WM-hypointensities": float(line.split()[3])})
+        elif "Left-non-WM-hypointensities" in line:
+            aseg.update({"aseg.Left-non-WM-hypointensities": float(line.split()[3])})
+        elif "Right-non-WM-hypointensities" in line:
+            aseg.update({"aseg.Right-non-WM-hypointensities": float(line.split()[3])})
+        elif "Optic-Chiasm" in line:
+            aseg.update({"aseg.Optic-Chiasm": float(line.split()[3])})
+        elif "CC_Posterior" in line:
+            aseg.update({"aseg.CC_Posterior": float(line.split()[3])})
+        elif "CC_Mid_Posterior" in line:
+            aseg.update({"aseg.CC_Mid_Posterior": float(line.split()[3])})
+        elif "CC_Central" in line:
+            aseg.update({"aseg.CC_Central": float(line.split()[3])})
+        elif "CC_Mid_Anterior" in line:
+            aseg.update({"aseg.CC_Mid_Anterior": float(line.split()[3])})
+        elif "CC_Anterior" in line:
+            aseg.update({"aseg.CC_Anterior": float(line.split()[3])})
+
+    # return
+    return aseg
+
+
+def readAparcStats(path_aparc_stats, hemi):
+    """
+    A function to read aparc.stats files.
+
+    """
+
+    # read file
+    with open(path_aparc_stats) as stats_file:
+        aparc_stats = stats_file.read().splitlines()
+
+    # read header
+    header = dict()
+    for line in aparc_stats:
+        if "# Measure Cortex, NumVert, " in line:
+            header.update({"aparc" + "." + hemi + "." + "CortexNumVert": float(line.split(",")[3])})
+        elif "# Measure Cortex, WhiteSurfArea," in line:
+            header.update({"aparc" + "." + hemi + "." + "CortexWhiteSurfArea": float(line.split(",")[3])})
+        elif "# Measure Cortex, MeanThickness," in line:
+            header.update({"aparc" + "." + hemi + "." + "CortexMeanThickness": float(line.split(",")[3])})
+        elif "# Measure BrainSeg," in line:
+            header.update({"aparc" + "." + hemi + "." + "BrainSeg": float(line.split(",")[3])})
+        elif "# Measure BrainSegNotVent," in line:
+            header.update({"aparc" + "." + hemi + "." + "BrainSegNotVent": float(line.split(",")[3])})
+        elif "# Measure BrainSegNotVentSurf," in line:
+            header.update({"aparc" + "." + hemi + "." + "BrainSegNotVentSurf": float(line.split(",")[3])})
+        elif "# Measure Cortex, CortexVol Total cortical gray matter volume," in line:
+            # missing comma in fs60 and fs71, hence [2]
+            header.update({"aparc" + "." + hemi + "." + "Cortex": float(line.split(",")[2])})
+        elif "# Measure SupraTentorial," in line:
+            header.update({"aparc" + "." + hemi + "." + "SupraTentorial": float(line.split(",")[3])})
+        elif "# Measure SupraTentorialNotVent," in line:
+            header.update({"aparc" + "." + hemi + "." + "SupraTentorialNotVent": float(line.split(",")[3])})
+        elif "# Measure EstimatedTotalIntraCranialVol," in line:
+            header.update({"aparc" + "." + hemi + "." + "EstimatedTotalIntraCranialVol": float(line.split(",")[3])})
+
+    # get column headers
+    col_headers = None
+    for line in aparc_stats:
+        if "# ColHeaders " in line:
+            col_headers = line.replace("# ColHeaders", "").split()
+
+    # read aparc
+    aparc = dict()
+    if col_headers is not None:
+        for line in aparc_stats:
+            if "bankssts" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "bankssts": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "caudalanteriorcingulate" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "caudalanteriorcingulate": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "caudalmiddlefrontal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "caudalmiddlefrontal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "cuneus" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "cuneus": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "entorhinal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "entorhinal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "fusiform" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "fusiform": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "inferiorparietal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "inferiorparietal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "inferiortemporal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "inferiortemporal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "isthmuscingulate" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "isthmuscingulate": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "lateraloccipital" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "lateraloccipital": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "lateralorbitofrontal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "lateralorbitofrontal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "lingual" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "lingual": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "medialorbitofrontal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "medialorbitofrontal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "middletemporal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "middletemporal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "parahippocampal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "parahippocampal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "paracentral" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "paracentral": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "parsopercularis" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "parsopercularis": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "parsorbitalis" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "parsorbitalis": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "parstriangularis" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "parstriangularis": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "pericalcarine" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "pericalcarine": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "postcentral" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "postcentral": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "posteriorcingulate" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "posteriorcingulate": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "precentral" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "precentral": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "precuneus" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "precuneus": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "rostralanteriorcingulate" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "rostralanteriorcingulate": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "rostralmiddlefrontal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "rostralmiddlefrontal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "superiorfrontal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "superiorfrontal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "superiorparietal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "superiorparietal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "superiortemporal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "superiortemporal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "supramarginal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "supramarginal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "frontalpole" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "frontalpole": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "temporalpole" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "temporalpole": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "transversetemporal" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "transversetemporal": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+            elif "insula" in line:
+                aparc.update(
+                    {
+                        "aparc"
+                        + "."
+                        + hemi
+                        + "."
+                        + "insula": dict(zip(col_headers[1:], [float(x) for x in line.split()[1:]]))
+                    }
+                )
+
+    # extract thickness values
+    if col_headers is not None:
+        thickness = dict()
+        for x in aparc.keys():
+            thickness[x] = aparc[x]["ThickAvg"]
+
+    # return
+    return header, aparc, thickness
+
+
+def readHypothalamusStats(path_hypothalamus_stats):
+    """
+    A function to read hypothalamic volume files.
+
+    """
+
+    # imports
+    import pandas as pd
+
+    # read file
+    hypothalamus_stats = pd.read_csv(path_hypothalamus_stats)
+
+    #
+    hypothalamus_stats.columns = "hypothalamus" + "." + hypothalamus_stats.columns
+
+    #
+    hypo = hypothalamus_stats.to_dict(orient="index")[0]
+
+    #
+    hypo.pop("hypothalamus.subject", None)
+
+    # return
+    return hypo
+
+
+def readHippocampusStats(path_hippocampus_stats, hemi, prefix):
+    """
+    A function to read hippocampal volume files.
+    Also works for amygdala volume files.
+
+    """
+
+    # imports
+    import pandas as pd
+
+    # read file
+    hippocampus_stats = pd.read_csv(path_hippocampus_stats, sep=" ", header=None, names=["volume"], index_col=0)
+
+    #
+    hippo = hippocampus_stats.transpose()
+
+    hippo.columns = prefix + "." + hemi + "." + hippo.columns
+
+    hippo = hippo.to_dict(orient="index")["volume"]
+
+    # return
+    return hippo
+
+
+# ------------------------------------------------------------------------------
+# outlier table
+
+
+def outlierTable():
+    """
+    A function to provide normative values for Freesurfer segmentations and
+    parcellations.
+
+    """
+
+    # define
+
+    outlierDict = dict(
+        [
+            ("Left-Accumbens-area", dict([("lower", 210.87844594754), ("upper", 718.01022026916)])),
+            ("Right-Accumbens-area", dict([("lower", 304.86134907845), ("upper", 751.63838456345)])),
+            ("Left-Amygdala", dict([("lower", 1179.73655974083), ("upper", 1935.09415214717)])),
+            ("Right-Amygdala", dict([("lower", 1161.54746836742), ("upper", 2002.14187676668)])),
+            ("Brain-Stem", dict([("lower", 18048.54263155760), ("upper", 25300.51090318110)])),
+            ("Left-Caudate", dict([("lower", 2702.73311142764), ("upper", 4380.54479618196)])),
+            ("Right-Caudate", dict([("lower", 2569.61140834210), ("upper", 4412.61035536070)])),
+            ("Left-Hippocampus", dict([("lower", 3432.26483953083), ("upper", 4934.43236139507)])),
+            ("Right-Hippocampus", dict([("lower", 3580.74371035841), ("upper", 5067.49668145829)])),
+            ("Left-Pallidum", dict([("lower", 935.47686324176), ("upper", 1849.42861796994)])),
+            ("Right-Pallidum", dict([("lower", 1078.14975428593), ("upper", 1864.08951102817)])),
+            ("Left-Putamen", dict([("lower", 3956.23134409153), ("upper", 6561.97642872937)])),
+            ("Right-Putamen", dict([("lower", 3768.88684356957), ("upper", 6142.52870810603)])),
+            ("Left-Thalamus-Proper", dict([("lower", 6483.36121320953), ("upper", 9489.46749012527)])),
+            ("Right-Thalamus-Proper", dict([("lower", 6065.70220487045), ("upper", 8346.88382091555)])),
+            ("Left-VentralDC", dict([("lower", 3182.42264293449), ("upper", 4495.77412707751)])),
+            ("Right-VentralDC", dict([("lower", 3143.88280953869), ("upper", 4407.63641978371)])),
+        ]
+    )
+
+    # return
+    return outlierDict
+
+
+# ------------------------------------------------------------------------------
+# main function
+
+
+def outlierDetection(
+    subjects,
+    subjects_dir,
+    output_dir,
+    outlierDict,
+    min_no_subjects=10,
+    hypothalamus=False,
+    hippocampus=False,
+    hippocampus_label=None,
+):
+    """
+    A function to evaluate potential outliers in the aseg.stats, [lr]h.aparc,
+    and optionally hypothalamic and hippocampal values.
+
+    """
+
+    # imports
+
+    import csv
+    import os
+
+    import numpy as np
+    import pandas as pd
+
+    # create a dictionary with all data from all subjects, and create a list of all available keys
+
+    regions = dict()
+
+    all_regions_keys = list()
+
+    for subject in subjects:
+        # aseg
+
+        path_aseg_stats = os.path.join(subjects_dir, subject, "stats", "aseg.stats")
+        aseg_stats = readAsegStats(path_aseg_stats)
+        regions[subject] = aseg_stats.copy()
+        all_regions_keys.extend(list(aseg_stats.keys()))
+
+        # aparc
+
+        path_aparc_stats = os.path.join(subjects_dir, subject, "stats", "lh.aparc.stats")
+        aparc_header, aparc_stats, aparc_thickness = readAparcStats(path_aparc_stats, hemi="lh")
+        regions[subject].update(aparc_thickness)
+        all_regions_keys.extend(list(aparc_thickness.keys()))
+
+        path_aparc_stats = os.path.join(subjects_dir, subject, "stats", "rh.aparc.stats")
+        aparc_header, aparc_stats, aparc_thickness = readAparcStats(path_aparc_stats, hemi="rh")
+        regions[subject].update(aparc_thickness)
+        all_regions_keys.extend(list(aparc_thickness.keys()))
+
+        # hypothalamus
+
+        if hypothalamus is True:
+            path_hypothalamus_stats = os.path.join(subjects_dir, subject, "mri", "hypothalamic_subunits_volumes.v1.csv")
+            if os.path.exists(path_hypothalamus_stats):
+                hypothalamus_stats = readHypothalamusStats(path_hypothalamus_stats)
+                regions[subject].update(hypothalamus_stats)
+                all_regions_keys.extend(list(hypothalamus_stats.keys()))
+
+        # hippocampus + amygdala
+
+        if hippocampus is True and hippocampus_label is not None:
+            path_hippocampus_stats = os.path.join(
+                subjects_dir, subject, "mri", "lh.hippoSfVolumes-" + hippocampus_label + ".txt"
+            )
+            if os.path.exists(path_hippocampus_stats):
+                hippocampus_stats = readHippocampusStats(path_hippocampus_stats, hemi="lh", prefix="hippocampus")
+                regions[subject].update(hippocampus_stats)
+                all_regions_keys.extend(list(hippocampus_stats.keys()))
+
+            path_hippocampus_stats = os.path.join(
+                subjects_dir, subject, "mri", "rh.hippoSfVolumes-" + hippocampus_label + ".txt"
+            )
+            if os.path.exists(path_hippocampus_stats):
+                hippocampus_stats = readHippocampusStats(path_hippocampus_stats, hemi="rh", prefix="hippocampus")
+                regions[subject].update(hippocampus_stats)
+                all_regions_keys.extend(list(hippocampus_stats.keys()))
+
+            path_hippocampus_stats = os.path.join(
+                subjects_dir, subject, "mri", "lh.amygNucVolumes-" + hippocampus_label + ".txt"
+            )
+            if os.path.exists(path_hippocampus_stats):
+                hippocampus_stats = readHippocampusStats(path_hippocampus_stats, hemi="lh", prefix="amygdala")
+                regions[subject].update(hippocampus_stats)
+                all_regions_keys.extend(list(hippocampus_stats.keys()))
+
+            path_hippocampus_stats = os.path.join(
+                subjects_dir, subject, "mri", "rh.amygNucVolumes-" + hippocampus_label + ".txt"
+            )
+            if os.path.exists(path_hippocampus_stats):
+                hippocampus_stats = readHippocampusStats(path_hippocampus_stats, hemi="rh", prefix="amygdala")
+                regions[subject].update(hippocampus_stats)
+                all_regions_keys.extend(list(hippocampus_stats.keys()))
+
+    # sort keys
+
+    all_regions_keys = set(all_regions_keys)
+
+    all_regions_keys = (
+        sorted(list(filter(lambda x: "aseg." in x, list(all_regions_keys))))
+        + sorted(list(filter(lambda x: "aparc." in x, list(all_regions_keys))))
+        + sorted(list(filter(lambda x: "hippocampus." in x, list(all_regions_keys))))
+        + sorted(list(filter(lambda x: "amygdala." in x, list(all_regions_keys))))
+        + sorted(list(filter(lambda x: "hypothalamus." in x, list(all_regions_keys))))
+    )
+
+    # compare individual data against sample statistics (if more than min_no_subjects cases)
+
+    outlierSampleNonpar = dict()
+    outlierSampleParam = dict()
+
+    outlierSampleNonparNum = dict()
+    outlierSampleParamNum = dict()
+
+    if len(subjects) >= min_no_subjects:
+        # compute means, sd, medians, and quantiles based on sample
+
+        df = pd.DataFrame.from_dict(regions).transpose()
+
+        iqr = np.percentile(df, 75, axis=0) - np.percentile(df, 25, axis=0)
+
+        sample_nonpar_lower = dict(zip(df.columns, np.percentile(df, 25, axis=0) - 1.5 * iqr))
+        sample_nonpar_upper = dict(zip(df.columns, np.percentile(df, 75, axis=0) + 1.5 * iqr))
+
+        sample_param_lower = dict(np.mean(df, axis=0) - 2 * np.std(df, axis=0))
+        sample_param_upper = dict(np.mean(df, axis=0) + 2 * np.std(df, axis=0))
+
+        # compare individual data against sample statistics
+
+        for subject in regions:
+            nonparDict = dict()
+            paramDict = dict()
+
+            for key in regions[subject]:
+                if (regions[subject][key] < sample_nonpar_lower[key]) or (
+                    regions[subject][key] > sample_nonpar_upper[key]
+                ):
+                    nonparDict.update({key: True})
+                else:
+                    nonparDict.update({key: False})
+
+                if (regions[subject][key] < sample_param_lower[key]) or (
+                    regions[subject][key] > sample_param_upper[key]
+                ):
+                    paramDict.update({key: True})
+                else:
+                    paramDict.update({key: False})
+
+            outlierSampleNonpar.update({subject: nonparDict})
+            outlierSampleParam.update({subject: paramDict})
+
+            outlierSampleNonparNum.update({subject: np.sum(list(nonparDict.values()))})
+            outlierSampleParamNum.update({subject: np.sum(list(paramDict.values()))})
+
+    else:
+        for subject in regions:
+            nonparDict = dict()
+            paramDict = dict()
+
+            for key in regions[subject]:
+                nonparDict.update({key: np.nan})
+                paramDict.update({key: np.nan})
+
+            outlierSampleNonpar.update({subject: nonparDict})
+            outlierSampleParam.update({subject: paramDict})
+
+            outlierSampleNonparNum.update({subject: np.nan})
+            outlierSampleParamNum.update({subject: np.nan})
+
+    # compare individual data against normative values
+
+    outlierNorms = dict()
+
+    outlierNormsNum = dict()
+
+    for subject in regions:
+        normsDict = dict()
+
+        for key in regions[subject]:
+            # no prefixes in outlier table
+            if key.startswith("aseg."):
+                outlierKey = key.replace("aseg.", "")
+            elif key.startswith("aparc."):
+                outlierKey = key.replace("aparc.", "")
+            elif key.startswith("hippocampus."):
+                outlierKey = key.replace("hippocampus.", "")
+            elif key.startswith("amygdala."):
+                outlierKey = key.replace("amygdala.", "")
+            elif key.startswith("hypothalamus."):
+                outlierKey = key.replace("aseg.", "")
+
+            if outlierKey in outlierDict:
+                if (regions[subject][key] < outlierDict[outlierKey]["lower"]) or (
+                    regions[subject][key] > outlierDict[outlierKey]["upper"]
+                ):
+                    normsDict.update({key: True})
+                else:
+                    normsDict.update({key: False})
+
+            else:
+                normsDict.update({key: np.nan})
+
+        outlierNorms.update({subject: normsDict})
+
+        outlierNormsNum.update({subject: np.nansum(list(normsDict.values()))})
+
+    # write to csv files
+
+    regionsFieldnames = ["subject"]
+    regionsFieldnames.extend(all_regions_keys)
+
+    with open(os.path.join(output_dir, "all.regions.stats"), "w") as datafile:
+        csvwriter = csv.DictWriter(
+            datafile, fieldnames=regionsFieldnames, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
+        )
+        csvwriter.writeheader()
+        for subject in sorted(list(regions.keys())):
+            tmp = regions[subject]
+            tmp.update({"subject": subject})
+            csvwriter.writerow(tmp)
+
+    with open(os.path.join(output_dir, "all.outliers.sample.nonpar.stats"), "w") as datafile:
+        csvwriter = csv.DictWriter(
+            datafile, fieldnames=regionsFieldnames, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
+        )
+        csvwriter.writeheader()
+        for subject in sorted(list(outlierSampleNonpar.keys())):
+            tmp = outlierSampleNonpar[subject]
+            tmp.update({"subject": subject})
+            csvwriter.writerow(tmp)
+
+    with open(os.path.join(output_dir, "all.outliers.sample.param.stats"), "w") as datafile:
+        csvwriter = csv.DictWriter(
+            datafile, fieldnames=regionsFieldnames, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
+        )
+        csvwriter.writeheader()
+        for subject in sorted(list(outlierSampleParam.keys())):
+            tmp = outlierSampleParam[subject]
+            tmp.update({"subject": subject})
+            csvwriter.writerow(tmp)
+
+    with open(os.path.join(output_dir, "all.outliers.norms.stats"), "w") as datafile:
+        csvwriter = csv.DictWriter(
+            datafile, fieldnames=regionsFieldnames, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
+        )
+        csvwriter.writeheader()
+        for subject in sorted(list(outlierNorms.keys())):
+            tmp = outlierNorms[subject]
+            tmp.update({"subject": subject})
+            csvwriter.writerow(tmp)
+
+    # return
+
+    return outlierSampleNonparNum, outlierSampleParamNum, outlierNormsNum
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..c16911e
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,74 @@
+[build-system]
+requires = ['setuptools >= 61.0.0']
+build-backend = 'setuptools.build_meta'
+
+[project]
+name = 'fsqc'
+description = 'A set of quality control scripts for Freesurfer- and Fastsurfer-processed structural MRI data'
+license = {file = 'LICENSE'}
+requires-python = '>=3.8'
+authors = [
+    {name = 'Kersten Diers', email = 'kersten.diers@dzne.de'},
+    {name = 'Martin Reuter', email = 'martin.reuter@dzne.de'}
+]
+maintainers = [
+    {name = 'Kersten Diers', email = 'kersten.diers@dzne.de'},
+    {name = 'Martin Reuter', email = 'martin.reuter@dzne.de'}
+]
+keywords = [
+    'Freesurfer',
+    'Fastsurfer',
+    'Quality control',
+    'Quality assurance'
+]
+classifiers = [
+    'Operating System :: Unix',
+    'Operating System :: MacOS',
+    'Programming Language :: Python :: 3 :: Only',
+    'Programming Language :: Python :: 3.8',
+    'Programming Language :: Python :: 3.9',
+    'Programming Language :: Python :: 3.10',
+    'Programming Language :: Python :: 3.11',
+    'Natural Language :: English',
+    'License :: OSI Approved :: MIT License',
+    'Intended Audience :: Science/Research',
+]
+dynamic = ["version", "readme", "dependencies"]
+
+[project.optional-dependencies]
+style = [
+    'flake8',
+]
+all = [
+    'fsqc[style]',
+]
+full = [
+    'fsqc[all]',
+]
+
+[project.urls]
+homepage = 'https://github.com/Deep-MI/fsqc'
+documentation = 'https://github.com/Deep-MI/fsqc'
+source = 'https://github.com/Deep-MI/fsqc'
+tracker = 'https://github.com/Deep-MI/fsqc/issues'
+
+[project.scripts]
+run_fsqc = 'fsqc.cli:main'
+
+[tool.pytest.ini_options]
+addopts = [
+    "--import-mode=importlib",
+]
+
+[tool.setuptools.dynamic]
+version = {file = 'VERSION'}
+readme = {file = ["README.md", "CHANGES.md"]}
+dependencies = {file = 'requirements.txt'}
+
+
+#[tool.setuptools] ### probably not needed
+#include-package-data = false
+
+[tool.setuptools.packages.find]
+include = ['fsqc', 'fsqc.cli']
+exclude = ['docker', 'singularity']
diff --git a/qatools.py b/qatools.py
deleted file mode 100755
index 41c49ee..0000000
--- a/qatools.py
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env python3
-
-# say hello
-print("")
-print("-----------------------------")
-print("qatools-python")
-print("-----------------------------")
-print("")
-
-# imports
-from qatoolspython import qatoolspython
-
-# parse arguments
-subjects_dir, output_dir, subjects, subjects_file, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer = qatoolspython._parse_arguments()
-
-# run qatools
-qatoolspython.run_qatools(subjects_dir, output_dir, subjects, subjects_file, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer)
diff --git a/qatoolspython/__init__.py b/qatoolspython/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/qatoolspython/createScreenshots.py b/qatoolspython/createScreenshots.py
deleted file mode 100644
index 6eeed32..0000000
--- a/qatoolspython/createScreenshots.py
+++ /dev/null
@@ -1,453 +0,0 @@
-"""
-This module provides a function to create screenshots
-
-"""
-
-# -----------------------------------------------------------------------------
-
-def createScreenshots(SUBJECT, SUBJECTS_DIR, OUTFILE, INTERACTIVE = True, LAYOUT = None,
-    BASE = ["default"], OVERLAY = ["default"], SURF = ["default"], SURFCOLOR = ["default"],
-    VIEWS = ["default"]
-    ):
-
-    """
-    function createScreenshots()
-
-    Requires FREESURFER_HOME environment variable
-
-    BASE, VIEWS must be lists, can be ["default"]
-
-    OVERLAY, SURF, SURFCOLOR can be lists or None, can be ["default"]
-
-    """
-
-    # -----------------------------------------------------------------------------
-    # auxiliary functions
-
-    def computeLayout(n):
-
-        import numpy as np
-
-        y = np.ceil(np.sqrt(n))
-
-        x = y - np.divmod(y**2 - n, y)[0]
-
-        return int(x), int(y)
-
-    # -----------------------------------------------------------------------------
-    # imports
-
-    import os
-    import matplotlib
-
-    import numpy as np
-    import pandas as pd
-    import nibabel as nb
-
-    if not INTERACTIVE:
-        matplotlib.use('Agg')
-
-    from matplotlib import pyplot as plt
-    from qatoolspython.qatoolspythonUtils import levelsetsTria
-
-    # -----------------------------------------------------------------------------
-    # settings
-
-    FIGSIZE = 8
-
-    FIGDPI = 100
-
-    ALPHA = 0.5
-
-    tol = 1e-16
-
-    # -----------------------------------------------------------------------------
-    # import image data
-
-    if BASE == ["default"]:
-        norm = nb.load(os.path.join(SUBJECTS_DIR, SUBJECT, 'mri', 'norm.mgz'))
-    else:
-        norm = nb.load(BASE[0])
-
-    if OVERLAY is None:
-        aseg = None
-    elif OVERLAY == ["default"]:
-        aseg = nb.load(os.path.join(SUBJECTS_DIR, SUBJECT, 'mri', 'aseg.mgz'))
-    else:
-        aseg = nb.load(OVERLAY[0])
-
-    # -----------------------------------------------------------------------------
-    # import surface data
-
-    if SURF == ["default"]:
-        surflist = [
-            os.path.join(SUBJECTS_DIR, SUBJECT, 'surf', 'lh.white'),
-            os.path.join(SUBJECTS_DIR, SUBJECT, 'surf', 'rh.white'),
-            os.path.join(SUBJECTS_DIR, SUBJECT, 'surf', 'lh.pial'),
-            os.path.join(SUBJECTS_DIR, SUBJECT, 'surf', 'rh.pial'),
-            ]
-    else:
-        surflist = SURF
-
-    surf = list()
-
-    if surflist is not None:
-        for i in range(len(surflist)):
-            surf.append(nb.freesurfer.io.read_geometry(surflist[i], read_metadata=True))
-
-    if SURFCOLOR == ["default"] and SURF == ["default"]:
-        surfcolor = ['yellow','yellow','red','red']
-    elif SURFCOLOR == ["default"] and SURF != ["default"]:
-        surfcolor = ['yellow'] * len(surf)
-    else:
-        surfcolor = SURFCOLOR
-
-    # -----------------------------------------------------------------------------
-    # import colortable, compute auxiliary variables, and transform to matplotlib
-    # colortable
-
-    lut = pd.read_csv(os.path.join(os.environ['FREESURFER_HOME'],'FreeSurferColorLUT.txt'),
-                      sep=' ',
-                      comment='#',
-                      header=None,
-                      skipinitialspace=True,
-                      skip_blank_lines=True,
-                      error_bad_lines=False,
-                      warn_bad_lines=True
-                      )
-
-    lut = np.array(lut)
-
-    # some fs7 labels are not present in fs6 LUT, check and add if necessary
-    if not(np.isin([801, 802, 803, 804, 805, 806, 807, 808, 809, 810], lut[:,0]).all()):
-        lutAdd = np.array((
-            [801, 'L_hypothalamus_anterior_inferior', 250, 255, 50, 0],
-            [802, 'L_hypothalamus_anterior_superior', 80, 200, 255, 0],
-            [803, 'L_hypothalamus_posterior', 255, 160, 0, 0],
-            [804, 'L_hypothalamus_tubular_inferior', 255, 160, 200, 0],
-            [805, 'L_hypothalamus_tubular_superior', 20, 180, 130, 0],
-            [806, 'R_hypothalamus_anterior_inferior', 250, 255, 50, 0],
-            [807, 'R_hypothalamus_anterior_superior', 80, 200, 255, 0],
-            [808, 'R_hypothalamus_posterior', 255, 160, 0, 0],
-            [809, 'R_hypothalamus_tubular_inferior', 255, 160, 200, 0],
-            [810, 'R_hypothalamus_tubular_superior', 20, 180, 130, 0]),
-            dtype=object)
-
-        lut = np.concatenate((lut, lutAdd), axis=0)
-
-    lutEnum = dict(zip(lut[:, 0], range(len(lut[:, 0]))))
-
-    lutTab = np.array(lut[:, (2, 3, 4, 5)] / 255, dtype="float32")
-    lutTab[:, 3] = 1
-
-    lutMap = matplotlib.colors.ListedColormap(lutTab)
-
-    # -----------------------------------------------------------------------------
-    # determine VIEWS
-
-    if VIEWS == ["default"]:
-        CutsRRAS = [('x',-10),('x',10),('y',0),('z',0)]
-    else:
-        CutsRRAS = VIEWS
-
-    # -----------------------------------------------------------------------------
-    # check if the chosen VIEWS are feasible. If not feasible changing to the nearest feasible values
-
-    m = norm.header.get_vox2ras_tkr()
-    n = norm.header.get_data_shape()
-
-    xyzIdx = np.array(
-        np.meshgrid(np.linspace(0, n[0] - 1, n[0]), np.linspace(0, n[1] - 1, n[1]), np.linspace(0, n[2] - 1, n[2])))
-    xyzIdxFlat3 = np.reshape(xyzIdx, (3, np.prod(n))).transpose()
-    xyzIdxFlat4 = np.hstack((xyzIdxFlat3, np.ones((np.prod(n), 1))))
-    rasIdxFlat3 = np.matmul(m, xyzIdxFlat4.transpose()).transpose()[:, 0:3]
-
-    for i, icr in enumerate(CutsRRAS):
-        if icr[0] == 'x':
-            iDim = 0
-        elif icr[0] == 'y':
-            iDim = 1
-        elif icr[0] == 'z':
-            iDim = 2
-
-        if not np.any(rasIdxFlat3[:, iDim] == icr[1]):
-            closestCutValue = rasIdxFlat3[np.abs(rasIdxFlat3[:, iDim] - icr[1]).argmin(), iDim]
-            print(f'INFO: the VIEW {icr} will be changed to (\'{icr[0]}\', {closestCutValue:.2f}) so it is not'
-                  ' necessary to interpolate volumetric data')
-            CutsRRAS[i] = (icr[0], closestCutValue)
-
-    # -----------------------------------------------------------------------------
-    # compute levelsets
-
-    LVL = list()
-
-    # will not run if surf is empty (intended)
-    for s in range(len(surf)):
-
-        sLVL = list()
-
-        for i in range(len(CutsRRAS)):
-
-            # determine dimension
-            if CutsRRAS[i][0] == 'x':
-                iDim = 0
-            elif CutsRRAS[i][0] == 'y':
-                iDim = 1
-            elif CutsRRAS[i][0] == 'z':
-                iDim = 2
-
-            # compute levelsets: e.g., LVL[SURF][VIEWS][vLVL|tLVL|iLVL][0][elementDim1][elementDim2]
-            sLVL.append(levelsetsTria(surf[s][0], surf[s][1], surf[s][0][:, iDim], CutsRRAS[i][1]))
-
-        LVL.append(sLVL)
-
-    # -----------------------------------------------------------------------------
-    # get data for norm
-
-    normData = norm.get_data()
-
-    normVals = normData
-
-    # -----------------------------------------------------------------------------
-    # get data for aseg and change to enumerated aseg so that it can be used as
-    # index to lutMap
-
-    if aseg is not None:
-
-        asegData = aseg.get_data()
-
-        asegUnique, asegIdx = np.unique(asegData, return_inverse=True)
-
-        asegEnum = np.array([lutEnum[x] for x in asegUnique])
-
-        asegVals = np.reshape(asegEnum[asegIdx], (aseg.shape))
-
-    # -----------------------------------------------------------------------------
-    # compile image data for plotting
-
-    # note that we are using the TkReg RAS system, not RAS per se
-    # this is because the surfaces are in TkReg RAS also
-
-    # one considerable advantage of the (TkReg) RAS system is that it is solely
-    # based on switching dimensions, not rotating at odd angles etc, so no need
-    # for interpolating.
-
-    # x_R y_R z_R c_R
-    # x_A y_A z_A c_A
-    # x_S y_S z_S c_S
-    #   0   0   0   1
-
-    # m = norm.header.get_vox2ras_tkr()
-    # n = norm.header.get_data_shape()
-
-    # xyzIdx = np.array(np.meshgrid(np.linspace(0, n[0] - 1, n[0]), np.linspace(0, n[1] - 1, n[1]), np.linspace(0, n[2] - 1, n[2])))
-    # xyzIdxFlat3 = np.reshape(xyzIdx, (3, np.prod(n))).transpose()
-    # xyzIdxFlat4 = np.hstack((xyzIdxFlat3, np.ones((np.prod(n), 1))))
-    # rasIdxFlat3 = np.matmul(m, xyzIdxFlat4.transpose()).transpose()[:, 0:3]
-
-    normValsRAS = list()
-    if aseg is not None:
-        asegValsRAS = list()
-
-    for i in range(len(CutsRRAS)):
-
-        # determine dimension
-        if CutsRRAS[i][0] == 'x':
-            iDim = 0
-        elif CutsRRAS[i][0] == 'y':
-            iDim = 1
-        elif CutsRRAS[i][0] == 'z':
-            iDim = 2
-
-        sel = np.array(xyzIdxFlat3[rasIdxFlat3[:, iDim] == CutsRRAS[i][1], :], dtype="int")
-        normValsRAS.append(np.squeeze(normVals[np.min(sel[:, 0]):np.max(sel[:, 0]) + 1, np.min(sel[:, 1]):np.max(sel[:, 1]) + 1, np.min(sel[:, 2]):np.max(sel[:, 2]) + 1]))
-        if aseg is not None:
-            asegValsRAS.append(np.squeeze(asegVals[np.min(sel[:, 0]):np.max(sel[:, 0]) + 1, np.min(sel[:, 1]):np.max(sel[:, 1]) + 1, np.min(sel[:, 2]):np.max(sel[:, 2]) + 1]))
-
-    # -----------------------------------------------------------------------------
-    # plotting: create a new figure, plot into it, then close it so it never gets
-    # displayed
-
-    # turn interactive plotting off unless interactive
-    if not INTERACTIVE:
-        plt.ioff()
-
-    # compute layout
-    if LAYOUT is None:
-        myLayout = computeLayout(len(CutsRRAS))
-    else:
-        myLayout = LAYOUT
-
-    # create list of de-facto layouts
-    myLayoutList = list()
-    for axsx in range(myLayout[0]):
-        for axsy in range(myLayout[1]):
-            myLayoutList.append((axsx,axsy))
-
-    # create subplots
-    fig, axs = plt.subplots(myLayout[0],myLayout[1])
-    axs = np.reshape(axs,myLayout)
-
-    # adjust layout
-    fig.set_size_inches([FIGSIZE*myLayout[1],FIGSIZE*myLayout[0]])
-    fig.set_dpi(FIGDPI)
-    fig.set_facecolor('black')
-    fig.set_tight_layout({'pad': 0})
-    fig.subplots_adjust(wspace=0)
-
-    # -----------------------------------------------------------------------------
-    # plot each panel
-
-    for p in range(len(CutsRRAS)):
-
-        print("Panel "+str(p))
-
-        axsx = myLayoutList[p][0]
-        axsy = myLayoutList[p][1]
-
-        # determine dimensions
-        if CutsRRAS[p][0] == 'x':
-            # x axis of the image should be towards anterior, y axis should be towards superior in RAS image
-            dims = (1, 2)
-            # determine extent
-            extent = (rasIdxFlat3[0, dims[0]], rasIdxFlat3[-1, dims[0]], rasIdxFlat3[0, dims[1]], rasIdxFlat3[-1, dims[1]])
-            # imshow puts the first dimension (rows) of the data on the y axis, and the second (columns) on the x axis
-            cor = np.where(m[dims[0], 0:3])[0]
-            axi = np.where(m[dims[1], 0:3])[0]
-            #
-            if axi < cor:
-                axs[axsx,axsy].imshow(normValsRAS[p], cmap='gray', origin='lower', extent=extent)
-                if aseg is not None:
-                    axs[axsx,axsy].imshow(asegValsRAS[p] + 0.5, cmap=lutMap, origin='lower', extent=extent, vmin=0, vmax=len(lutTab), alpha=ALPHA)
-            else:
-                axs[axsx,axsy].imshow(normValsRAS[p].transpose(), cmap='gray', origin='lower', extent=extent)
-                if aseg is not None:
-                    axs[axsx,axsy].imshow(asegValsRAS[p].transpose() + 0.5, cmap=lutMap, origin='lower', extent=extent, vmin=0, vmax=len(lutTab), alpha=ALPHA)
-        elif CutsRRAS[p][0] == 'y':
-            # x axis of the image should be towards right, y axis should be towards superior in RAS image
-            dims = (0, 2)
-            # determine extent
-            extent = (rasIdxFlat3[0, dims[0]], rasIdxFlat3[-1, dims[0]], rasIdxFlat3[0, dims[1]], rasIdxFlat3[-1, dims[1]])
-            # imshow puts the first dimension (rows) of the data on the y axis, and the second (columns) on the x axis
-            sag = np.where(m[dims[0], 0:3])[0]
-            axi = np.where(m[dims[1], 0:3])[0]
-            #
-            if axi < sag:
-                axs[axsx,axsy].imshow(normValsRAS[p], cmap='gray', origin='lower', extent=extent)
-                if aseg is not None:
-                    axs[axsx,axsy].imshow(asegValsRAS[p] + 0.5, cmap=lutMap, origin='lower', extent=extent, vmin=0, vmax=len(lutTab), alpha=ALPHA)
-            else:
-                axs[axsx,axsy].imshow(normValsRAS[p].transpose(), cmap='gray', origin='lower', extent=extent)
-                if aseg is not None:
-                    axs[axsx,axsy].imshow(asegValsRAS[p].transpose() + 0.5, cmap=lutMap, origin='lower', extent=extent, vmin=0, vmax=len(lutTab), alpha=ALPHA)
-        elif CutsRRAS[p][0] == 'z':
-            # x axis of the image should be towards right, y axis should be towards anterior in RAS image
-            dims = (0, 1)
-            # determine extent
-            extent = (rasIdxFlat3[0, dims[0]], rasIdxFlat3[-1, dims[0]], rasIdxFlat3[0, dims[1]], rasIdxFlat3[-1, dims[1]])
-            # imshow puts the first dimension (rows) of the data on the y axis, and the second (columns) on the x axis
-            sag = np.where(m[dims[0], 0:3])[0]
-            cor = np.where(m[dims[1], 0:3])[0]
-            if cor < sag:
-                axs[axsx,axsy].imshow(normValsRAS[p], cmap='gray', origin='lower', extent=extent)
-                if aseg is not None:
-                    axs[axsx,axsy].imshow(asegValsRAS[p] + 0.5, cmap=lutMap, origin='lower', extent=extent, vmin=0, vmax=len(lutTab), alpha=ALPHA)
-            else:
-                axs[axsx,axsy].imshow(normValsRAS[p].transpose(), cmap='gray', origin='lower', extent=extent)
-                if aseg is not None:
-                    axs[axsx,axsy].imshow(asegValsRAS[p].transpose() + 0.5, cmap=lutMap, origin='lower', extent=extent, vmin=0, vmax=len(lutTab), alpha=ALPHA)
-
-        # prepare plot
-        if rasIdxFlat3[0, dims[0]] > rasIdxFlat3[-1, dims[0]]:
-            axs[axsx,axsy].invert_xaxis()
-        if rasIdxFlat3[0, dims[1]] > rasIdxFlat3[-1, dims[1]]:
-            axs[axsx,axsy].invert_yaxis()
-
-        axs[axsx,axsy].set_axis_off()
-        axs[axsx,axsy].set_aspect('equal')
-
-        # now plot
-        for s in range(len(surf)):
-
-            if len(LVL[s][p][0][0]) > 0:
-
-                print("Surface "+str(s))
-
-                # create array of line segments
-                tmpx = list()
-                tmpy = list()
-
-                for i in range(len(LVL[s][p][1][0])):
-                    tmpx.append((LVL[s][p][0][0][LVL[s][p][1][0][i][0] - 1][dims[0]], LVL[s][p][0][0][LVL[s][p][1][0][i][1] - 1][dims[0]]))
-                    tmpy.append((LVL[s][p][0][0][LVL[s][p][1][0][i][0] - 1][dims[1]], LVL[s][p][0][0][LVL[s][p][1][0][i][1] - 1][dims[1]]))
-
-                tmpx = np.array(tmpx)
-                tmpy = np.array(tmpy)
-
-                # remove duplicate points
-                tmpxy = np.unique(np.concatenate((tmpx, tmpy), axis=1), axis=0)
-                tmpx = tmpxy[:, 0:2]
-                tmpy = tmpxy[:, 2:4]
-
-                # remove segments which are de-facto points
-                tmpIdx = np.logical_or(np.abs(tmpx[:,0]-tmpx[:,1])>tol, np.abs(tmpy[:,0]-tmpy[:,1])>tol)
-                tmpx = tmpx[tmpIdx, :]
-                tmpy = tmpy[tmpIdx, :]
-
-                # need to order array of line segments; whenever we encounter a
-                # closed loop, we will already plot; otherwise, plot in the end
-                sortIdx = np.array(range(0, len(tmpx)))
-
-                tmpxSort = np.array(tmpx[sortIdx[0], ], ndmin=2)
-                tmpySort = np.array(tmpy[sortIdx[0], ], ndmin=2)
-
-                sortIdx = np.delete(sortIdx, sortIdx[0])
-
-                while len(sortIdx) > 1:
-
-                    findIdx = np.array(np.where(np.logical_and(
-                        np.abs(tmpx[sortIdx, ] - tmpxSort[tmpxSort.shape[0]-1, 1]) < tol,
-                        np.abs(tmpy[sortIdx, ] - tmpySort[tmpySort.shape[0]-1, 1]) < tol)), ndmin=2).T
-
-                    # delete existing finds
-                    findIdxKeep = list()
-                    for k in range(findIdx.shape[0]):
-                        if not np.any(np.all(np.logical_or(tmpx[sortIdx[findIdx[k, 0]], 0] == tmpxSort, tmpx[sortIdx[findIdx[k, 0]], 1] == tmpxSort), axis=1)):
-                            findIdxKeep.append(k)
-                    findIdx = findIdx[findIdxKeep, ]
-
-                    if findIdx.shape[0] == 0:
-                        # close loop and plot already
-                        axs[axsx,axsy].plot(tmpxSort, tmpySort, color=surfcolor[s], linewidth=np.round(FIGSIZE/8))
-                        # reset (start new loop)
-                        tmpxSort = np.array(tmpx[sortIdx[0], ], ndmin=2)
-                        tmpySort = np.array(tmpy[sortIdx[0], ], ndmin=2)
-                        sortIdx = np.delete(sortIdx, 0)
-                    elif findIdx.shape[0] == 1:
-                        # add to current set
-                        if findIdx[0, 1] == 0:
-                            tmpxSort = np.append(tmpxSort, np.array(tmpx[sortIdx[findIdx[0, 0]], ::1], ndmin=2), axis=0)
-                            tmpySort = np.append(tmpySort, np.array(tmpy[sortIdx[findIdx[0, 0]], ::1], ndmin=2), axis=0)
-                        elif findIdx[0, 1] == 1:
-                            tmpxSort = np.append(tmpxSort, np.array(tmpx[sortIdx[findIdx[0, 0]], ::-1], ndmin=2), axis=0)
-                            tmpySort = np.append(tmpySort, np.array(tmpy[sortIdx[findIdx[0, 0]], ::-1], ndmin=2), axis=0)
-                        sortIdx = np.delete(sortIdx, findIdx[0, 0])
-                    elif findIdx.shape[0] > 1:
-                        # error
-                        print("A problem occurred with the surface overlays")
-                        break
-
-                # now final plot
-                axs[axsx,axsy].plot(tmpxSort, tmpySort, color=surfcolor[s], linewidth=np.round(FIGSIZE/8))
-
-    # -----------------------------------------------------------------------------
-    # output
-
-    if not INTERACTIVE:
-        plt.savefig(OUTFILE, facecolor=fig.get_facecolor())
-        plt.close(fig)
-
-    # -----------------------------------------------------------------------------
-    #
-
-    print()
diff --git a/qatoolspython/evaluateFornixSegmentation.py b/qatoolspython/evaluateFornixSegmentation.py
deleted file mode 100644
index 0a89d73..0000000
--- a/qatoolspython/evaluateFornixSegmentation.py
+++ /dev/null
@@ -1,213 +0,0 @@
-"""
-This module provides a function to evaluate potential missegmentation of the fornix
-
-"""
-
-# -----------------------------------------------------------------------------
-
-def evaluateFornixSegmentation(SUBJECT, SUBJECTS_DIR, OUTPUT_DIR, CREATE_SCREENSHOT = True, SCREENSHOTS_OUTFILE = [], RUN_SHAPEDNA = True, N_EIGEN = 15):
-    """
-    A function to evaluate potential missegmentation of the fornix.
-
-    This script evaluates potential missegmentation of the fornix, which may 
-    erroneously be attached to the 'corpus collosum' label.
-
-    It will run Freesurfer's 'mri_convert' script to apply the cc_up.lta 
-    transform to the norm.mgz and the aseg files, and create a binary corpus 
-    callosum mask and surface. Resulting files are saved to subject-specific
-    directories witin the 'fornix' subdirectory of the output directory.
-
-    If the corresponding arguments are set to 'True', the script will also 
-    create screenshots and run a shape analysis of the corpus callosum surface.
-    Resulting files will be saved to the same directory as indicated above.
-
-    Required arguments:
-        - SUBJECT
-        - SUBJECTS_DIR
-        - OUTPUT_DIR
-
-    Optional arguments:
-        - CREATE_SCREENSHOT <bool> (default: True)
-        - SCREENSHOTS_OUTFILE <string> or empty list (default: [])
-        - RUN_SHAPEDNA <bool> (default: True)
-        - N_EIGEN <int> number of Eigenvalues for shape analyis (default: 30)
-
-    Requires (if not found, returns NaNs):
-        - mri/transforms/cc_up.lta
-        - mri/aseg.mgz
-        - mri/norm.mgz
-
-    Returns:
-        - a numpy array of N_EIGEN eigenvalues if RUN_SHAPEDNA is True, 
-          otherwise a numpy array of NaNs of the same dimension
-
-    """
-
-    # --------------------------------------------------------------------------
-    # imports
-
-    import os
-    import sys
-    import shlex
-    import subprocess
-    import numpy as np
-    from qatoolspython.createScreenshots import createScreenshots
-
-    # --------------------------------------------------------------------------
-    # auxiliary functions
-
-    def split_callback(option, opt, value, parser):
-      setattr(parser.values, option.dest, value.split(','))
-
-    def which(program):
-        def is_exe(fpath):
-            return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
-        fpath, fname = os.path.split(program)
-        if fpath:
-            if is_exe(program):
-                return program
-        else:
-            for path in os.environ["PATH"].split(os.pathsep):
-                path = path.strip('"')
-                exe_file = os.path.join(path, program)
-                if is_exe(exe_file):
-                    return exe_file
-            if is_exe(os.path.join('.',program)):
-                return os.path.join('.',program)
-
-        return None
-
-    def my_print(message):
-        """
-        print message, then flush stdout
-        """
-        print(message)
-        sys.stdout.flush()
-
-    def run_cmd(cmd,err_msg):
-        """
-        execute the comand
-        """
-        clist = cmd.split()
-        progname=which(clist[0])
-        if (progname) is None:
-            my_print('ERROR: '+ clist[0] +' not found in path!')
-            sys.exit(1)
-        clist[0]=progname
-        cmd = ' '.join(clist)
-        my_print('#@# Command: ' + cmd+'\n')
-
-        args = shlex.split(cmd)
-        try:
-            subprocess.check_call(args)
-        except subprocess.CalledProcessError as e:
-            my_print('ERROR: '+err_msg)
-            #sys.exit(1)
-            raise
-        my_print('\n')
-
-    # --------------------------------------------------------------------------
-    # main part
-
-
-    # check files
-
-    if not os.path.isfile(os.path.join(SUBJECTS_DIR,SUBJECT,"mri","transforms","cc_up.lta")):
-
-        print('WARNING: could not find '+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","transforms","cc_up.lta")+', returning NaNs')
-
-        out = np.empty(N_EIGEN)
-        out[:] = np.nan
-
-        return out
-
-    elif not os.path.isfile(os.path.join(SUBJECTS_DIR,SUBJECT,"mri","aseg.mgz")):
-
-        print('WARNING: could not find '+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","aseg.mgz")+', returning NaNs')
-
-        out = np.empty(N_EIGEN)
-        out[:] = np.nan
-
-        return out
-
-    elif not os.path.isfile(os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")):
-
-        print('WARNING: could not find '+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")+', returning NaNs')
-
-        out = np.empty(N_EIGEN)
-        out[:] = np.nan
-
-        return out
-        
-    if not SCREENSHOTS_OUTFILE:
-        SCREENSHOTS_OUTFILE = os.path.join(OUTPUT_DIR,"cc.png")
-
-    ## run make_upright; note: rather than 'make_upright', better use 'mri_cc' 
-    ## to compute the transformation matrix, should this ever be necessary.
-    #
-    #cmd = "make_upright  "+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")+" "+os.path.join(OUTPUT_DIR,"normCCup.mgz")+" "+os.path.join(OUTPUT_DIR,"cc_up.lta")
-    #run_cmd(cmd,"Could not run make_upright")
-
-    # convert lta to xfm (need to adjust some directories when using make_upright)
-
-    cmd = "lta_convert --inlta "+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","transforms","cc_up.lta")+" --outmni "+os.path.join(OUTPUT_DIR,"cc_up.xfm")
-    run_cmd(cmd,"Could not convert lta")
-
-    # conduct transform for aseg and norm
-
-    cmd = "mri_convert -i "+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","aseg.mgz")+" -at "+os.path.join(OUTPUT_DIR,"cc_up.xfm")+" -rt nearest -o "+os.path.join(OUTPUT_DIR,"asegCCup.mgz")
-    run_cmd(cmd,"Could not conduct cc_up.xfm transform")
-
-    # when using 'make_upright', conducting the transform for norm.mgz is no
-    # longer necessary (and will produce the same results)
-    cmd = "mri_convert -i "+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")+" -at "+os.path.join(OUTPUT_DIR,"cc_up.xfm")+" -rt cubic -o "+os.path.join(OUTPUT_DIR,"normCCup.mgz")
-    run_cmd(cmd,"Could not conduct cc_up.xfm transform")
-
-    # create fornix mask and surface
-
-    cmd = "mri_binarize --i "+os.path.join(OUTPUT_DIR,"asegCCup.mgz")+" --match 251 252 253 254 255 --dilate 2 --erode 2 --surf "+os.path.join(OUTPUT_DIR,"cc.surf")+" --surf-smooth 1 --o "+os.path.join(OUTPUT_DIR,"cc.mgz")
-    run_cmd(cmd,"Could not create fornix mask and surface")
-
-    # --------------------------------------------------------------------------
-    # create screenshot
-
-    if CREATE_SCREENSHOT is True:
-        createScreenshots(SUBJECT = SUBJECT, SUBJECTS_DIR = SUBJECTS_DIR, 
-            INTERACTIVE = False, VIEWS = [('x', -2), ('x', 0), ('x', 2)], LAYOUT = (1, 3),
-            BASE = [os.path.join(OUTPUT_DIR,"normCCup.mgz")], OVERLAY = [os.path.join(OUTPUT_DIR,"cc.mgz")], SURF = [os.path.join(OUTPUT_DIR,"cc.surf")], OUTFILE = SCREENSHOTS_OUTFILE)
-
-    # --------------------------------------------------------------------------
-    # run shapeDNA
-
-    if RUN_SHAPEDNA is True:
-
-        import nibabel as nb
-
-        from brainPrintPython import laplaceTria
-
-        surf = nb.freesurfer.io.read_geometry(os.path.join(OUTPUT_DIR,"cc.surf"), read_metadata=True)
-
-        ev, evec = laplaceTria(surf[0], surf[1], k=N_EIGEN)
-
-        d = dict()
-        d['Refine'] = 0
-        d['Degree'] = 1
-        d['Dimension'] = 2
-        d['Elements'] = len(surf[1])
-        d['DoF'] = len(surf[0])
-        d['NumEW'] = N_EIGEN
-        d['Eigenvalues'] = ev
-        d['Eigenvectors'] = evec
-
-        # return
-        return d['Eigenvalues']
-
-    else:
-
-        out = np.empty(N_EIGEN)
-        out[:] = np.nan
-
-        return out
-
-
diff --git a/qatoolspython/evaluateHypothalamicSegmentation.py b/qatoolspython/evaluateHypothalamicSegmentation.py
deleted file mode 100644
index 5e1a4d3..0000000
--- a/qatoolspython/evaluateHypothalamicSegmentation.py
+++ /dev/null
@@ -1,167 +0,0 @@
-"""
-This module provides a function to evaluate potential missegmentation of the hypothalamus
-
-"""
-
-# -----------------------------------------------------------------------------
-
-def evaluateHypothalamicSegmentation(SUBJECT, SUBJECTS_DIR, OUTPUT_DIR, CREATE_SCREENSHOT = True, SCREENSHOTS_OUTFILE = []):
-    """
-    A function to evaluate potential missegmentation of the hypothalamus.
-
-    This script evaluates potential missegmentation of the hypothalamus as
-    created by FreeSurfer 7.2's module for hypothalamic segmentation:
-    https://surfer.nmr.mgh.harvard.edu/fswiki/HypothalamicSubunits.
-
-    If the corresponding arguments are set to 'True', the script will also
-    create screenshots. Resulting files will be saved to the same directory
-    as indicated above.
-
-    Required arguments:
-        - SUBJECT
-        - SUBJECTS_DIR
-        - OUTPUT_DIR
-
-    Optional arguments:
-        - CREATE_SCREENSHOT <bool> (default: True)
-        - SCREENSHOTS_OUTFILE <string> or empty list (default: [])
-
-    Requires (if not found, returns NaNs):
-        - mri/hypothalamic_subunits_seg.v1.mgz
-        - mri/norm.mgz
-
-    """
-
-    # --------------------------------------------------------------------------
-    # imports
-
-    import os
-    import sys
-    import shlex
-    import subprocess
-    import numpy as np
-    from qatoolspython.createScreenshots import createScreenshots
-
-    # --------------------------------------------------------------------------
-    # auxiliary functions
-
-    def split_callback(option, opt, value, parser):
-      setattr(parser.values, option.dest, value.split(','))
-
-    def which(program):
-        def is_exe(fpath):
-            return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
-        fpath, fname = os.path.split(program)
-        if fpath:
-            if is_exe(program):
-                return program
-        else:
-            for path in os.environ["PATH"].split(os.pathsep):
-                path = path.strip('"')
-                exe_file = os.path.join(path, program)
-                if is_exe(exe_file):
-                    return exe_file
-            if is_exe(os.path.join('.',program)):
-                return os.path.join('.',program)
-
-        return None
-
-    def my_print(message):
-        """
-        print message, then flush stdout
-        """
-        print(message)
-        sys.stdout.flush()
-
-    def run_cmd(cmd,err_msg):
-        """
-        execute the comand
-        """
-        clist = cmd.split()
-        progname=which(clist[0])
-        if (progname) is None:
-            my_print('ERROR: '+ clist[0] +' not found in path!')
-            sys.exit(1)
-        clist[0]=progname
-        cmd = ' '.join(clist)
-        my_print('#@# Command: ' + cmd+'\n')
-
-        args = shlex.split(cmd)
-        try:
-            subprocess.check_call(args)
-        except subprocess.CalledProcessError as e:
-            my_print('ERROR: '+err_msg)
-            #sys.exit(1)
-            raise
-        my_print('\n')
-
-    # --------------------------------------------------------------------------
-    # main part
-
-    # check files
-
-    if not os.path.isfile(os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")):
-
-        print('ERROR: could not find '+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")+', not running hypothalamus module.')
-
-        return
-
-    if not os.path.isfile(os.path.join(SUBJECTS_DIR,SUBJECT,"mri","hypothalamic_subunits_seg.v1.mgz")):
-
-        print('ERROR: could not find '+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","hypothalamic_subunits_seg.v1.mgz")+', not running hypothalamus module.')
-
-        return
-
-    if not SCREENSHOTS_OUTFILE:
-        SCREENSHOTS_OUTFILE = os.path.join(OUTPUT_DIR,"hypothalamus.png")
-
-    # create mask and surface
-
-    cmd = "mri_binarize --i "+os.path.join(SUBJECTS_DIR,SUBJECT,"mri","hypothalamic_subunits_seg.v1.mgz")+" --match 801 802 803 804 805 806 807 808 809 810 --surf "+os.path.join(OUTPUT_DIR,"hypothalamus.surf")+" --surf-smooth 1 --o "+os.path.join(OUTPUT_DIR,"hypothalamus.mgz")
-    run_cmd(cmd,"Could not create hypothalamus mask and surface")
-
-    # get centroids
-    if which("mri_segcentroids") is None:
-
-        # fs6 version
-        centroids = list()
-
-        for i in range(801, 811):
-
-            #
-            cmd = "mri_vol2label --i " + os.path.join(SUBJECTS_DIR,SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz") + " --id " + str(i) + " --l " + os.path.join(OUTPUT_DIR, "hypothalamus_labels_" + str(i) + ".txt")
-            run_cmd(cmd, "Could not create hypothalamus centroids")
-
-            #
-            dat = np.loadtxt(os.path.join(OUTPUT_DIR, "hypothalamus_labels_" + str(i) + ".txt.label"), skiprows=2)
-
-            #
-            centroids.append(np.mean(dat, axis=0))
-
-        centroids = np.array(centroids)
-
-    else:
-
-        # fs7 version
-        cmd = "mri_segcentroids --i " + os.path.join(SUBJECTS_DIR, SUBJECT, "mri", "hypothalamic_subunits_seg.v1.mgz") + " --o " + os.path.join(OUTPUT_DIR, "hypothalamus_centroids.txt")
-        run_cmd(cmd, "Could not create hypothalamus centroids")
-
-        #
-        centroids = np.loadtxt(os.path.join(OUTPUT_DIR,"hypothalamus_centroids.txt"), skiprows=2)
-
-    centroids_x0 = centroids[3,1]
-    centroids_x1 = centroids[8,1]
-    centroids_y0 = (centroids[0,2]+centroids[5,2])/2
-    centroids_y1 = (centroids[1,2]+centroids[6,2])/2
-    centroids_y2 = (centroids[3,2]+centroids[8,2])/2
-    centroids_y3 = (centroids[4,2]+centroids[9,2])/2
-    centroids_y4 = (centroids[2,2]+centroids[7,2])/2
-    centroids_z0 = (centroids[2,3]+centroids[7,3])/2
-    centroids_z1 = (centroids[4,3]+centroids[9,3])/2
-
-    # --------------------------------------------------------------------------
-    # create screenshot
-
-    if CREATE_SCREENSHOT is True:
-        createScreenshots(SUBJECT = SUBJECT, SUBJECTS_DIR = SUBJECTS_DIR, INTERACTIVE = False, VIEWS = [('x', centroids_x0), ('x', centroids_x1), ('y', centroids_y0), ('y', centroids_y1), ('y', centroids_y2), ('y', centroids_y3), ('y', centroids_y4), ('z', centroids_z0), ('z', centroids_z1)], LAYOUT = (1, 9), BASE = [os.path.join(SUBJECTS_DIR,SUBJECT,"mri","norm.mgz")], OVERLAY = [os.path.join(SUBJECTS_DIR,SUBJECT,"mri","hypothalamic_subunits_seg.v1.mgz")], SURF = None, OUTFILE = SCREENSHOTS_OUTFILE)
diff --git a/qatoolspython/outlierDetection.py b/qatoolspython/outlierDetection.py
deleted file mode 100644
index ced307f..0000000
--- a/qatoolspython/outlierDetection.py
+++ /dev/null
@@ -1,363 +0,0 @@
-"""
-This module provides a function to evaluate potential outliers in the aseg.stats
-values.
-
-"""
-
-# ------------------------------------------------------------------------------
-# subfunctions
-
-def readAsegStats(path_aseg_stats):
-    """
-    A function to read aseg.stats files.
-
-    """
-
-    # read file
-    with open(path_aseg_stats) as stats_file:
-        aseg_stats = stats_file.read().splitlines()
-
-    # initialize
-    aseg = dict()
-
-    # read measures
-    for line in aseg_stats:
-        if '# Measure BrainSeg,' in line:
-            aseg.update({'BrainSeg' : float(line.split(',')[3])})
-        elif '# Measure BrainSegNotVent,' in line:
-            aseg.update({'BrainSegNotVent' : float(line.split(',')[3])})
-        elif '# Measure BrainSegNotVentSurf,' in line:
-            aseg.update({'BrainSegNotVentSurf' : float(line.split(',')[3])})
-        elif '# Measure VentricleChoroidVol,' in line:
-            aseg.update({'VentricleChoroidVol' : float(line.split(',')[3])})
-        elif '# Measure lhCortex,' in line:
-            aseg.update({'lhCortex' : float(line.split(',')[3])})
-        elif '# Measure rhCortex,' in line:
-            aseg.update({'rhCortex' : float(line.split(',')[3])})
-        elif '# Measure Cortex,' in line:
-            aseg.update({'Cortex' : float(line.split(',')[3])})
-        elif '# Measure lhCerebralWhiteMatter,' in line:
-            aseg.update({'lhCerebralWhiteMatter' : float(line.split(',')[3])})
-        elif '# Measure rhCerebralWhiteMatter,' in line:
-            aseg.update({'rhCerebralWhiteMatter' : float(line.split(',')[3])})
-        elif '# Measure CerebralWhiteMatter,' in line:
-            aseg.update({'CerebralWhiteMatter' : float(line.split(',')[3])})
-        elif '# Measure SubCortGray,' in line:
-            aseg.update({'SubCortGray' : float(line.split(',')[3])})
-        elif '# Measure TotalGray,' in line:
-            aseg.update({'TotalGray' : float(line.split(',')[3])})
-        elif '# Measure SupraTentorial,' in line:
-            aseg.update({'SupraTentorial' : float(line.split(',')[3])})
-        elif '# Measure SupraTentorialNotVent,' in line:
-            aseg.update({'SupraTentorialNotVent' : float(line.split(',')[3])})
-        elif '# Measure SupraTentorialNotVentVox,' in line:
-            aseg.update({'SupraTentorialNotVentVox' : float(line.split(',')[3])})
-        elif '# Measure Mask,' in line:
-            aseg.update({'Mask' : float(line.split(',')[3])})
-        elif '# Measure BrainSegVol-to-eTIV,' in line:
-            aseg.update({'BrainSegVol_to_eTIV' : float(line.split(',')[3])})
-        elif '# Measure MaskVol-to-eTIV,' in line:
-            aseg.update({'MaskVol_to_eTIV' : float(line.split(',')[3])})
-        elif '# Measure lhSurfaceHoles,' in line:
-            aseg.update({'lhSurfaceHoles' : float(line.split(',')[3])})
-        elif '# Measure rhSurfaceHoles,' in line:
-            aseg.update({'rhSurfaceHoles' : float(line.split(',')[3])})
-        elif '# Measure SurfaceHoles,' in line:
-            aseg.update({'SurfaceHoles' : float(line.split(',')[3])})
-        elif '# Measure EstimatedTotalIntraCranialVol,' in line:
-            aseg.update({'EstimatedTotalIntraCranialVol' : float(line.split(',')[3])})
-        elif 'Left-Lateral-Ventricle' in line:
-            aseg.update({'Left-Lateral-Ventricle' : float(line.split()[3])})
-        elif 'Left-Inf-Lat-Vent' in line:
-            aseg.update({'Left-Inf-Lat-Vent' : float(line.split()[3])})
-        elif 'Left-Cerebellum-White-Matter' in line:
-            aseg.update({'Left-Cerebellum-White-Matter' : float(line.split()[3])})
-        elif 'Left-Cerebellum-Cortex' in line:
-            aseg.update({'Left-Cerebellum-Cortex' : float(line.split()[3])})
-        elif 'Left-Thalamus-Proper' in line:
-            aseg.update({'Left-Thalamus-Proper' : float(line.split()[3])})
-        elif 'Left-Caudate' in line:
-            aseg.update({'Left-Caudate' : float(line.split()[3])})
-        elif 'Left-Putamen' in line:
-            aseg.update({'Left-Putamen' : float(line.split()[3])})
-        elif 'Left-Pallidum' in line:
-            aseg.update({'Left-Pallidum' : float(line.split()[3])})
-        elif '3rd-Ventricle' in line:
-            aseg.update({'3rd-Ventricle' : float(line.split()[3])})
-        elif '4th-Ventricle' in line:
-            aseg.update({'4th-Ventricle' : float(line.split()[3])})
-        elif 'Brain-Stem' in line:
-            aseg.update({'Brain-Stem' : float(line.split()[3])})
-        elif 'Left-Hippocampus' in line:
-            aseg.update({'Left-Hippocampus' : float(line.split()[3])})
-        elif 'Left-Amygdala' in line:
-            aseg.update({'Left-Amygdala' : float(line.split()[3])})
-        elif 'CSF' in line:
-            aseg.update({'CSF' : float(line.split()[3])})
-        elif 'Left-Accumbens-area' in line:
-            aseg.update({'Left-Accumbens-area' : float(line.split()[3])})
-        elif 'Left-VentralDC' in line:
-            aseg.update({'Left-VentralDC' : float(line.split()[3])})
-        elif 'Left-vessel' in line:
-            aseg.update({'Left-vessel' : float(line.split()[3])})
-        elif 'Left-choroid-plexus' in line:
-            aseg.update({'Left-choroid-plexus' : float(line.split()[3])})
-        elif 'Right-Lateral-Ventricle' in line:
-            aseg.update({'Right-Lateral-Ventricle' : float(line.split()[3])})
-        elif 'Right-Inf-Lat-Vent' in line:
-            aseg.update({'Right-Inf-Lat-Vent' : float(line.split()[3])})
-        elif 'Right-Cerebellum-White-Matter' in line:
-            aseg.update({'Right-Cerebellum-White-Matter' : float(line.split()[3])})
-        elif 'Right-Cerebellum-Cortex' in line:
-            aseg.update({'Right-Cerebellum-Cortex' : float(line.split()[3])})
-        elif 'Right-Thalamus-Proper' in line:
-            aseg.update({'Right-Thalamus-Proper' : float(line.split()[3])})
-        elif 'Right-Caudate' in line:
-            aseg.update({'Right-Caudate' : float(line.split()[3])})
-        elif 'Right-Putamen' in line:
-            aseg.update({'Right-Putamen' : float(line.split()[3])})
-        elif 'Right-Pallidum' in line:
-            aseg.update({'Right-Pallidum' : float(line.split()[3])})
-        elif 'Right-Hippocampus' in line:
-            aseg.update({'Right-Hippocampus' : float(line.split()[3])})
-        elif 'Right-Amygdala' in line:
-            aseg.update({'Right-Amygdala' : float(line.split()[3])})
-        elif 'Right-Accumbens-area' in line:
-            aseg.update({'Right-Accumbens-area' : float(line.split()[3])})
-        elif 'Right-VentralDC' in line:
-            aseg.update({'Right-VentralDC' : float(line.split()[3])})
-        elif 'Right-vessel' in line:
-            aseg.update({'Right-vessel' : float(line.split()[3])})
-        elif 'Right-choroid-plexus' in line:
-            aseg.update({'Right-choroid-plexus' : float(line.split()[3])})
-        elif '5th-Ventricle' in line:
-            aseg.update({'5th-Ventricle' : float(line.split()[3])})
-        elif 'WM-hypointensities' in line:
-            aseg.update({'WM-hypointensities' : float(line.split()[3])})
-        elif 'Left-WM-hypointensities' in line:
-            aseg.update({'Left-WM-hypointensities' : float(line.split()[3])})
-        elif 'Right-WM-hypointensities' in line:
-            aseg.update({'Right-WM-hypointensities' : float(line.split()[3])})
-        elif 'non-WM-hypointensities' in line:
-            aseg.update({'non-WM-hypointensities' : float(line.split()[3])})
-        elif 'Left-non-WM-hypointensities' in line:
-            aseg.update({'Left-non-WM-hypointensities' : float(line.split()[3])})
-        elif 'Right-non-WM-hypointensities' in line:
-            aseg.update({'Right-non-WM-hypointensities' : float(line.split()[3])})
-        elif 'Optic-Chiasm' in line:
-            aseg.update({'Optic-Chiasm' : float(line.split()[3])})
-        elif 'CC_Posterior' in line:
-            aseg.update({'CC_Posterior' : float(line.split()[3])})
-        elif 'CC_Mid_Posterior' in line:
-            aseg.update({'CC_Mid_Posterior' : float(line.split()[3])})
-        elif 'CC_Central' in line:
-            aseg.update({'CC_Central' : float(line.split()[3])})
-        elif 'CC_Mid_Anterior' in line:
-            aseg.update({'CC_Mid_Anterior' : float(line.split()[3])})
-        elif 'CC_Anterior' in line:
-            aseg.update({'CC_Anterior' : float(line.split()[3])})
-
-    # return
-    return aseg
-
-# ------------------------------------------------------------------------------
-# outlier table
-
-def outlierTable():
-    """
-    A function to provide normative values for Freesurfer segmentations and 
-    parcellations.
-
-    """
-
-    # define
-
-    outlierDict = dict([
-        ('Left-Accumbens-area',   dict([('lower' ,    210.87844594754), ('upper',   718.01022026916)])),
-        ('Right-Accumbens-area',  dict([('lower' ,    304.86134907845), ('upper',   751.63838456345)])),
-        ('Left-Amygdala',         dict([('lower' ,   1179.73655974083), ('upper',  1935.09415214717)])),
-        ('Right-Amygdala',        dict([('lower' ,   1161.54746836742), ('upper',  2002.14187676668)])),
-        ('Brain-Stem',            dict([('lower' ,  18048.54263155760), ('upper', 25300.51090318110)])),
-        ('Left-Caudate',          dict([('lower' ,   2702.73311142764), ('upper',  4380.54479618196)])),
-        ('Right-Caudate',         dict([('lower' ,   2569.61140834210), ('upper',  4412.61035536070)])),
-        ('Left-Hippocampus',      dict([('lower' ,   3432.26483953083), ('upper',  4934.43236139507)])),
-        ('Right-Hippocampus',     dict([('lower' ,   3580.74371035841), ('upper',  5067.49668145829)])),
-        ('Left-Pallidum',         dict([('lower' ,    935.47686324176), ('upper',  1849.42861796994)])),
-        ('Right-Pallidum',        dict([('lower' ,   1078.14975428593), ('upper',  1864.08951102817)])),
-        ('Left-Putamen',          dict([('lower' ,   3956.23134409153), ('upper',  6561.97642872937)])),
-        ('Right-Putamen',         dict([('lower' ,   3768.88684356957), ('upper',  6142.52870810603)])),
-        ('Left-Thalamus-Proper',  dict([('lower' ,   6483.36121320953), ('upper',  9489.46749012527)])),
-        ('Right-Thalamus-Proper', dict([('lower' ,   6065.70220487045), ('upper',  8346.88382091555)])),
-        ('Left-VentralDC',        dict([('lower' ,   3182.42264293449), ('upper',  4495.77412707751)])),
-        ('Right-VentralDC',       dict([('lower' ,   3143.88280953869), ('upper',  4407.63641978371)]))
-        ])
-
-    # return
-    return outlierDict
-
-# ------------------------------------------------------------------------------
-# main function
-
-def outlierDetection(subjects, subjects_dir, output_dir, outlierDict, min_no_subjects=10):
-    """
-    A function to evaluate potential outliers in the aseg.stats values.
-
-    """
-
-    # imports
-
-    import os
-    import csv
-    import numpy as np
-    import pandas as pd
-    from qatoolspython.outlierDetection import readAsegStats
-
-    # create a dictionary with all data from all subjects, and create a list of all available keys
-
-    aseg = dict()
-
-    all_aseg_keys = list()
-
-    for subject in subjects:
-
-        path_aseg_stats = os.path.join(subjects_dir, subject, "stats", "aseg.stats")
-        aseg_stats = readAsegStats(path_aseg_stats)
-
-        aseg.update({subject : aseg_stats})
-
-        all_aseg_keys.extend(list(aseg_stats.keys()))
-
-    all_aseg_keys = list(sorted(set(all_aseg_keys)))
-
-    # compare individual data against sample statistics (if more than min_no_subjects cases)
-
-    outlierSampleNonpar = dict()
-    outlierSampleParam = dict()
-
-    outlierSampleNonparNum = dict()
-    outlierSampleParamNum = dict()
-
-    if len(subjects) >= min_no_subjects:
-
-        # compute means, sd, medians, and quantiles based on sample
-
-        df = pd.DataFrame.from_dict(aseg).transpose()
-
-        iqr = np.percentile(df, 75, axis=0) - np.percentile(df, 25, axis=0)
-
-        sample_nonpar_lower = dict(zip(df.columns, np.percentile(df, 25, axis=0) - 1.5 * iqr))
-        sample_nonpar_upper = dict(zip(df.columns, np.percentile(df, 75, axis=0) + 1.5 * iqr))
-
-        sample_param_lower = dict(np.mean(df, axis=0) - 2 * np.std(df, axis=0))
-        sample_param_upper = dict(np.mean(df, axis=0) + 2 * np.std(df, axis=0))
-
-        # compare individual data against sample statistics
-
-        for subject in aseg:
-
-            nonparDict = dict()
-            paramDict = dict()
-
-            for key in aseg[subject]:
-
-                if (aseg[subject][key] < sample_nonpar_lower[key]) or (aseg[subject][key] > sample_nonpar_upper[key]):
-                    nonparDict.update({key : True })
-                else:
-                    nonparDict.update({key : False })
-
-                if (aseg[subject][key] < sample_param_lower[key]) or (aseg[subject][key] > sample_param_upper[key]):
-                    paramDict.update({key : True })
-                else:
-                    paramDict.update({key : False })
-
-            outlierSampleNonpar.update({subject : nonparDict})
-            outlierSampleParam.update({subject: paramDict})
-
-            outlierSampleNonparNum.update({subject : np.sum(list(nonparDict.values()))})
-            outlierSampleParamNum.update({subject : np.sum(list(paramDict.values()))})
-
-    else:
-
-        for subject in aseg:
-
-            nonparDict = dict()
-            paramDict = dict()
-
-            for key in aseg[subject]:
-
-                nonparDict.update({key : np.nan })
-                paramDict.update({key : np.nan })
-
-            outlierSampleNonpar.update({subject : nonparDict})
-            outlierSampleParam.update({subject: paramDict})
-
-            outlierSampleNonparNum.update({subject: np.nan})
-            outlierSampleParamNum.update({subject: np.nan})
-
-    # compare individual data against normative values
-
-    outlierNorms = dict()
-
-    outlierNormsNum = dict()
-
-    for subject in aseg:
-
-        normsDict = dict()
-
-        for key in aseg[subject]:
-
-            if key in outlierDict:
-
-                if (aseg[subject][key] < outlierDict[key]['lower']) or (aseg[subject][key] > outlierDict[key]['upper']):
-                    normsDict.update({key: True})
-                else:
-                    normsDict.update({key: False})
-
-            else:
-
-                normsDict.update({key: np.nan})
-
-        outlierNorms.update({subject : normsDict})
-
-        outlierNormsNum.update({subject: np.nansum(list(normsDict.values()))})
-
-    # write to csv files
-
-    asegFieldnames = ['subject']
-    asegFieldnames.extend(all_aseg_keys)
-
-    with open(os.path.join(output_dir, 'all.aseg.stats'), 'w') as datafile:
-        csvwriter = csv.DictWriter(datafile, fieldnames=asegFieldnames, delimiter=',',quotechar='"', quoting=csv.QUOTE_MINIMAL)
-        csvwriter.writeheader()
-        for subject in sorted(list(aseg.keys())):
-            tmp = aseg[subject]
-            tmp.update({'subject' : subject})
-            csvwriter.writerow(tmp)
-
-    with open(os.path.join(output_dir, 'all.outliers.sample.nonpar.stats'), 'w') as datafile:
-        csvwriter = csv.DictWriter(datafile, fieldnames=asegFieldnames, delimiter=',',quotechar='"', quoting=csv.QUOTE_MINIMAL)
-        csvwriter.writeheader()
-        for subject in sorted(list(outlierSampleNonpar.keys())):
-            tmp = outlierSampleNonpar[subject]
-            tmp.update({'subject' : subject})
-            csvwriter.writerow(tmp)
-
-    with open(os.path.join(output_dir, 'all.outliers.sample.param.stats'), 'w') as datafile:
-        csvwriter = csv.DictWriter(datafile, fieldnames=asegFieldnames, delimiter=',',quotechar='"', quoting=csv.QUOTE_MINIMAL)
-        csvwriter.writeheader()
-        for subject in sorted(list(outlierSampleParam.keys())):
-            tmp = outlierSampleParam[subject]
-            tmp.update({'subject' : subject})
-            csvwriter.writerow(tmp)
-
-    with open(os.path.join(output_dir, 'all.outliers.norms.stats'), 'w') as datafile:
-        csvwriter = csv.DictWriter(datafile, fieldnames=asegFieldnames, delimiter=',',quotechar='"', quoting=csv.QUOTE_MINIMAL)
-        csvwriter.writeheader()
-        for subject in sorted(list(outlierNorms.keys())):
-            tmp = outlierNorms[subject]
-            tmp.update({'subject' : subject})
-            csvwriter.writerow(tmp)
-
-    # return
-
-    return outlierSampleNonparNum, outlierSampleParamNum, outlierNormsNum
diff --git a/qatoolspython/qatoolspython.py b/qatoolspython/qatoolspython.py
deleted file mode 100644
index 58a20d1..0000000
--- a/qatoolspython/qatoolspython.py
+++ /dev/null
@@ -1,1358 +0,0 @@
-"""
-This module provides the main functionality of the qatoolspython package.
-
-"""
-
-# ==============================================================================
-# FUNCTIONS
-
-# ------------------------------------------------------------------------------
-# get_help()
-
-def get_help(print_help=True, return_help=False):
-    """
-    a function to return a help message
-
-    """
-
-    HELPTEXT="""
-
-    qatools-python
-
-
-    ============
-    Description:
-    ============
-
-    This is a set of quality assurance / quality control scripts for Freesurfer
-    processed structural MRI data.
-
-    It is a revision, extension, and translation to the Python language of the
-    original Freesurfer QA Tools that are provided at
-    https://surfer.nmr.mgh.harvard.edu/fswiki/QATools
-
-    It has been augmented by additional functions from the MRIQC toolbox, available
-    at https://github.com/poldracklab/mriqc and https://osf.io/haf97, and with code
-    derived from the shapeDNA and brainPrint toolboxes, available at
-    https://reuter.mit.edu.
-
-    The core functionality of this toolbox is to compute the following features:
-
-    - wm_snr_orig   ...  signal-to-noise for white matter in orig.mgz
-    - gm_snr_orig   ...  signal-to-noise for gray matter in orig.mgz
-    - wm_snr_norm   ...  signal-to-noise for white matter in norm.mgz
-    - gm_snr_norm   ...  signal-to-noise for gray matter in norm.mgz
-    - cc_size       ...  relative size of the corpus callosum
-    - holes_lh      ...  number of holes in the left hemisphere
-    - holes_rh      ...  number of holes in the right hemisphere
-    - defects_lh    ...  number of defects in the left hemisphere
-    - defects_rh    ...  number of defects in the right hemisphere
-    - topo_lh       ...  topological fixing time for the left hemisphere
-    - topo_rh       ...  topological fixing time for the right hemisphere
-    - con_snr_lh    ...  wm/gm contrast signal-to-noise ratio in the left hemisphere
-    - con_snr_rh    ...  wm/gm contrast signal-to-noise ratio in the right hemisphere
-    - rot_tal_x     ...  rotation component of the Talairach transform around the x axis
-    - rot_tal_y     ...  rotation component of the Talairach transform around the y axis
-    - rot_tal_z     ...  rotation component of the Talairach transform around the z axis
-
-    The program will use an existing output directory (or try to create it) and
-    write a csv table into that location. The csv table will contain the above
-    metrics plus a subject identifier.
-
-    In addition to the core functionality of the toolbox there are several optional
-    modules that can be run according to need:
-
-    - screenshots module
-
-    This module allows for the automated generation of cross-sections of the brain
-    that are overlaid with the anatomical segmentations (asegs) and the white and
-    pial surfaces. These images will be saved to the 'screenshots' subdirectory
-    that will be created within the output directory. These images can be used for
-    quickly glimpsing through the processing results. Note that no display manager
-    is required for this module, i.e. it can be run on a remote server, for example.
-
-    - fornix module
-
-    This is a module to assess potential issues with the segmentation of the
-    corpus callosum, which may incorrectly include parts of the fornix. To assess
-    segmentation quality, a screenshot of the contours of the corpus callosum
-    segmentation overlaid on the norm.mgz will be saved in the 'fornix'
-    subdirectory of the output directory.
-
-    - hypothalamus module
-
-    This is a module to assess potential issues with the segmentation of the
-    hypothalamus. To assess segmentation quality, a screenshot of the hypothalamus
-    segmentation overlaid on the norm.mgz will be saved in the 'hypothalamus'
-    subdirectory of the output directory.
-
-    - shape module
-
-    The shape module will run a shapeDNA / brainprint analysis to compute distances
-    of shape descriptors between lateralized brain structures. This can be used
-    to identify discrepancies and irregularities between pairs of corresponding
-    structures. The results will be included in the main csv table, and the output
-    directory will also contain a "brainprint" subdirectory.
-
-    - outlier module
-
-    This is a module to detect extreme values among the subcortical ('aseg')
-    segmentations. The outlier detection is based on comparisons with the
-    distributions of the sample as well as normative values taken from the
-    literature (see References).
-
-    For comparisons with the sample distributions, extreme values are defined in
-    two ways: nonparametrically, i.e. values that are 1.5 times the interquartile
-    range below or above the 25th or 75th percentile of the sample, respectively,
-    and parametrically, i.e. values that are more than 2 standard deviations above
-    or below the sample mean. Note that a minimum of 5 supplied subjects is
-    required for running these analyses, otherwise `NaNs` will be returned.
-
-    For comparisons with the normative values, lower and upper bounds are computed
-    from the 95% prediction intervals of the regression models given in Potvin et
-    al., 1996, and values exceeding these bounds will be flagged. As an
-    alternative, users may specify their own normative values by using the
-    '--outlier-table' argument. This requires a custom csv table with headers
-    `label`, `upper`, and `lower`, where `label` indicates a column of anatomical
-    names. It can be a subset and the order is arbitrary, but naming must exactly
-    match the nomenclature of the 'aseg.stats' file. `upper` and `lower` are user-
-    specified upper and lower bounds.
-
-    The main csv table will be appended with the following summary variables, and
-    more detailed output about will be saved as csv tables in the 'outliers'
-    subdirectory of the main output directory.
-
-    n_outliers_sample_nonpar ... number of structures that are 1.5 times the IQR
-                                 above/below the 75th/25th percentile
-    n_outliers_sample_param  ... number of structures that are 2 SD above/below
-                                 the mean
-    n_outliers_norms         ... number of structures exceeding the upper and
-                                 lower bounds of the normative values
-
-
-    ======
-    Usage:
-    ======
-
-        python3 qatools.py --subjects_dir <directory> --output_dir <directory>
-                                  [--subjects SubjectID [SubjectID ...]]
-                                  [--subjects-file <file>]
-                                  [--screenshots] [--screenshots-html]
-                                  [--fornix] [--fornix-html] [--hypothalamus]
-                                  [--hypothalamus-html] [--shape]
-                                  [--outlier] [--fastsurfer] [-h]
-
-        required arguments:
-          --subjects_dir <directory>
-                                subjects directory with a set of Freesurfer
-                                processed individual datasets.
-          --output_dir <directory>
-                                output directory
-
-        optional arguments:
-          --subjects SubjectID [SubjectID ...]
-                                list of subject IDs
-          --subjects-file <file>
-                                filename of a file with subject IDs (one per line)
-          --screenshots         create screenshots of individual brains
-          --screenshots-html    create html summary page of screenshots (requires
-                                --screenshots)
-          --fornix              check fornix segmentation
-          --fornix-html          create html summary page of fornix evaluation (requires
-                                 --fornix)
-          --hypothalamus        check hypothalamic segmentation
-          --hypothalamus-html   create html summary page of hypothalamic evaluation
-                                (requires --hypothalamus)
-          --shape               run shape analysis
-          --outlier             run outlier detection
-          --outlier-table       specify normative values (only in conjunction with
-                                --outlier)
-          --fastsurfer          use FastSurfer instead of FreeSurfer output
-
-        getting help:
-          -h, --help            display this help message and exit
-
-        expert options:
-          --screenshots_base <image>
-                                filename of an image that should be used instead of
-                                norm.mgz as the base image for the screenshots. Can be
-                                an individual file (which would not be appropriate for
-                                multi-subject analysis) or can be a file without
-                                pathname and with the same filename across subjects
-                                within the 'mri' subdirectory of an individual FreeSurfer
-                                results directory (which would be approprite for multi-
-                                subject analysis).
-          --screenshots_overlay <image>
-                                path to an image that should be used instead of
-                                aseg.mgz as the overlay image for the screenshots;
-                                can also be none. Can be an individual file (which would
-                                not be appropriate for multi-subject analysis) or can be
-                                a file without pathname and with the same filename across
-                                subjects within the 'mri' subdirectory of an individual
-                                FreeSurfer results directory (which would be approprite
-                                for multi-subject analysis).
-          --screenshots_surf <surf> [<surf> ...]
-                                one or more surface files that should be used instead
-                                of [lr]h.white and [lr]h.pial; can also be none.
-                                Can be one or more individual file(s) (which would not be
-                                appropriate for multi-subject analysis) or can be a (list
-                                of) file(s) without pathname and with the same filename
-                                across subjects within the 'surf' subdirectory of an
-                                individual FreeSurfer results directory (which would be
-                                approprite for multi-subject analysis).
-          --screenshots_views <view> [<view> ...]
-                                one or more views to use for the screenshots in
-                                the form of x=<numeric> y=<numeric> and/or
-                                z=<numeric>. order does not matter. default views
-                                are x=-10 x=10 y=0 z=0.
-          --screenshots_layout <rows> <columns>
-                                layout matrix for screenshot images
-
-
-    ========================
-    Use as a python package:
-    ========================
-
-    As an alternative to their command-line usage, the qc scripts can also be run
-    within a pure python environment, i.e. installed and imported as a python
-    package.
-
-    Use `import qatoolspython` (or sth. equivalent) to import the package within a
-    python environment.
-
-    Use the `run_qatools` function from the `qatoolspython` module to run an analysis:
-
-    `from qatoolspython import qatoolspython`
-
-    `qatoolspython.run_qatools(subjects_dir='/my/subjects/dir', output_dir='/my/output/dir')`
-
-    See `help(qatoolspython)` for further usage info and options.
-
-
-    =============
-    Known Issues:
-    =============
-
-    The program will analyze recon-all logfiles, and may fail or return erroneous
-    results if the logfile is append by multiple restarts of recon-all runs.
-    Ideally, the logfile should therefore consist of just a single, successful
-    recon-all run.
-
-
-    ========
-    Authors:
-    ========
-
-    - qatools-python: Kersten Diers, Tobias Wolff, and Martin Reuter.
-    - Freesurfer QA Tools: David Koh, Stephanie Lee, Jenni Pacheco, Vasanth Pappu,
-      and Louis Vinke.
-    - shapeDNA and brainPrint toolboxes: Martin Reuter
-
-
-    ===========
-    References:
-    ===========
-
-    Esteban O, Birman D, Schaer M, Koyejo OO, Poldrack RA, Gorgolewski KJ; MRIQC:
-    Advancing the Automatic Prediction of Image Quality in MRI from Unseen Sites;
-    PLOS ONE 12(9):e0184661; doi:10.1371/journal.pone.0184661.
-
-    Wachinger C, Golland P, Kremen W, Fischl B, Reuter M; 2015; BrainPrint: a
-    Discriminative Characterization of Brain Morphology; Neuroimage: 109, 232-248;
-    doi:10.1016/j.neuroimage.2015.01.032.
-
-    Reuter M, Wolter FE, Shenton M, Niethammer M; 2009; Laplace-Beltrami
-    Eigenvalues and Topological Features of Eigenfunctions for Statistical Shape
-    Analysis; Computer-Aided Design: 41, 739-755, doi:10.1016/j.cad.2009.02.007.
-
-    Potvin O, Mouiha A, Dieumegarde L, Duchesne S, & Alzheimer's Disease Neuroimaging
-    Initiative; 2016; Normative data for subcortical regional volumes over the lifetime
-    of the adult human brain; Neuroimage: 137, 9-20;
-    doi.org/10.1016/j.neuroimage.2016.05.016
-
-    =============
-    Requirements:
-    =============
-
-    A working installation of Freesurfer 6.0 or later must be sourced.
-
-    At least one subject whose structural MR image was processed with Freesurfer
-    6.0 or later.
-
-    A Python version >= 3.5 is required to run this script.
-
-    Required packages include (among others) the nibabel and skimage package for
-    the core functionality, plus the the matplotlib, pandas, and transform3d
-    packages for some optional functions and modules. See the `requirements.txt`
-    file for a complete list. Use `pip3 install -r requirements.txt` to install
-    these packages.
-
-    For the shape analysis module, the brainprint and lapy packages from
-    https://github.com/Deep-MI are required (both version 0.2 or newer).
-
-    This software has been tested on Ubuntu 16.04, CentOS7, and MacOS 10.14.
-
-
-    ========
-    License:
-    ========
-
-    This software is licensed under the MIT License, see associated LICENSE file
-    for details.
-
-    Copyright (c) 2019 Image Analysis Group, DZNE e.V.
-
-    """
-
-    if print_help:
-        print(HELPTEXT)
-
-    if return_help:
-        return HELPTEXT
-
-
-# ------------------------------------------------------------------------------
-# parse_arguments
-
-def _parse_arguments():
-    """
-    an internal function to parse input arguments
-
-    """
-
-    # imports
-    import sys
-    import argparse
-
-    # parse
-    parser = argparse.ArgumentParser(description='''
-        This program takes existing Freesurfer analysis results of one
-        or more subjects and computes a set of quality metrics. These will be
-        reported in a summary csv table.
-
-        For a description of these metrics, see the gitlab/github page or the
-        header section of this script.
-        ''',
-        add_help=False, formatter_class=argparse.RawTextHelpFormatter)
-
-    required = parser.add_argument_group('required arguments')
-    required.add_argument('--subjects_dir', dest="subjects_dir", help="subjects directory with a set of Freesurfer \nprocessed individual datasets.", metavar="<directory>", required=True)
-    required.add_argument('--output_dir', dest="output_dir", help="output directory", metavar="<directory>", required=True)
-
-    optional = parser.add_argument_group('optional arguments')
-    optional.add_argument('--subjects', dest="subjects", help="list of subject IDs. If omitted, all suitable sub-\ndirectories witin the subjects directory will be \nused.", default=None, nargs='+', metavar="SubjectID", required=False)
-    optional.add_argument('--subjects-file', dest="subjects_file", help="filename with list of subject IDs (one per line). \nIf omitted, all suitable sub-directories witin \nthe subjects directory will be used.", default=None, metavar="<filename>", required=False)
-    optional.add_argument('--shape', dest='shape', help="run shape analysis", default=False, action="store_true", required=False)
-    optional.add_argument('--screenshots', dest='screenshots', help="create screenshots of individual brains", default=False, action="store_true", required=False)
-    optional.add_argument('--screenshots-html', dest='screenshots_html', help="create html summary page for screenshots", default=False, action="store_true", required=False)
-    optional.add_argument('--screenshots_base', dest='screenshots_base', help=argparse.SUPPRESS, default="default", metavar="<base image for screenshots>", required=False) # this is currently a hidden "expert" option
-    optional.add_argument('--screenshots_overlay', dest='screenshots_overlay', help=argparse.SUPPRESS, default="default", metavar="<overlay image for screenshots>", required=False) # this is currently a hidden "expert" option
-    optional.add_argument('--screenshots_surf', dest='screenshots_surf', help=argparse.SUPPRESS, default="default", nargs="+", metavar="<surface(s) for screenshots>", required=False) # this is currently a hidden "expert" option
-    optional.add_argument('--screenshots_views', dest='screenshots_views', help=argparse.SUPPRESS, default="default", nargs="+", metavar="<dimension=coordinate [dimension=coordinate]>", required=False) # this is currently a hidden "expert" option
-    optional.add_argument('--screenshots_layout', dest='screenshots_layout', help=argparse.SUPPRESS, default=None, nargs=2, metavar="<rows> <columns>", required=False) # this is currently a hidden "expert" option
-    optional.add_argument('--fornix', dest='fornix', help="check fornix segmentation", default=False, action="store_true", required=False)
-    optional.add_argument('--fornix-html', dest='fornix_html', help="create html summary page for fornix evaluation", default=False, action="store_true", required=False)
-    optional.add_argument('--hypothalamus', dest='hypothalamus', help="check hypothalamus segmentation", default=False, action="store_true", required=False)
-    optional.add_argument('--hypothalamus-html', dest='hypothalamus_html', help="create html summary page for hypothalamus evaluation", default=False, action="store_true", required=False)
-    optional.add_argument('--outlier', dest='outlier', help="run outlier detection", default=False, action="store_true", required=False)
-    optional.add_argument('--outlier-table', dest="outlier_table", help="specify normative values", default=None, metavar="<filename>", required=False)
-    optional.add_argument('--fastsurfer', dest='fastsurfer', help="use FastSurfer output", default=False, action="store_true", required=False)
-
-    help = parser.add_argument_group('getting help')
-    help.add_argument('-h', '--help', help="display this help message and exit", action='help')
-
-    # check if there are any inputs; if not, print help and exit
-    if len(sys.argv)==1:
-        args = parser.parse_args(['--help'])
-    else:
-        args = parser.parse_args()
-
-    return args.subjects_dir, args.output_dir, args.subjects, \
-        args.subjects_file, args.shape, args.screenshots, \
-        args.screenshots_html, args.screenshots_base, \
-        args.screenshots_overlay, args.screenshots_surf, \
-        args.screenshots_views, args.screenshots_layout, args.fornix, \
-        args.fornix_html, args.hypothalamus, \
-        args.hypothalamus_html, args.outlier, args.outlier_table, args.fastsurfer
-
-# ------------------------------------------------------------------------------
-# check arguments
-
-def _check_arguments(subjects_dir, output_dir, subjects, subjects_file, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer):
-    """
-    an internal function to check input arguments
-
-    """
-
-    # --------------------------------------------------------------------------
-    # imports
-
-    import os
-    import sys
-    import errno
-
-    import tempfile
-    import importlib.util
-
-    # --------------------------------------------------------------------------
-    # check arguments
-
-    # check if subject directory exists
-    if os.path.isdir(subjects_dir):
-        print("Found subjects directory", subjects_dir)
-    else:
-        print('ERROR: subjects directory '+subjects_dir+' is not an existing directory\n')
-        sys.exit(1)
-
-    # check if output directory exists or can be created and is writable
-    if os.path.isdir(output_dir):
-        print("Found output directory", output_dir)
-    else:
-        try:
-            os.mkdir(output_dir)
-        except:
-            print('ERROR: cannot create output directory '+output_dir+'\n')
-            sys.exit(1)
-
-        try:
-            testfile = tempfile.TemporaryFile(dir=output_dir)
-            testfile.close()
-        except OSError as e:
-            if e.errno != errno.EACCES:  # 13
-                e.filename = output_dir
-                raise
-            print('\nERROR: '+output_dir+' not writeable (check access)!\n')
-            sys.exit(1)
-
-    # check if both subjects and subjects-file were specified
-    if subjects is not None and subjects_file is not None:
-        print("ERROR: Use either --subjects or --subjects-file (but not both).")
-        sys.exit(1)
-
-    # check if subjects-file exists and get data
-    if subjects_file is not None:
-        if os.path.isfile(subjects_file):
-            # read file
-            with open(subjects_file) as subjects_file_f:
-                subjects = subjects_file_f.read().splitlines()
-        else:
-            print("ERROR: Could not find subjects file", subjects_file)
-            sys.exit(1)
-
-    # if neither subjects nor subjects_file are given, get contents of the subject
-    # directory and check if aseg.stats (as a proxy) exists
-    if subjects is None and subjects_file is None:
-        subjects = []
-        for subject in os.listdir(subjects_dir):
-            path_aseg_stat = os.path.join(subjects_dir, subject, "stats", "aseg.stats")
-            if os.path.isfile(path_aseg_stat):
-                print("Found subject", subject)
-                subjects.extend([subject])
-
-    # check if screenshots subdirectory exists or can be created and is writable
-    if screenshots is True:
-        if os.path.isdir(os.path.join(output_dir, 'screenshots')):
-            print("Found screenshots directory", os.path.join(output_dir, 'screenshots'))
-        else:
-            try:
-                os.mkdir(os.path.join(output_dir, 'screenshots'))
-            except:
-                print('ERROR: cannot create screenshots directory '+os.path.join(output_dir, 'screenshots')+'\n')
-                sys.exit(1)
-
-            try:
-                testfile = tempfile.TemporaryFile(dir=os.path.join(output_dir, 'screenshots'))
-                testfile.close()
-            except OSError as e:
-                if e.errno != errno.EACCES:  # 13
-                    e.filename = os.path.join(output_dir, 'screenshots')
-                    raise
-                print('\nERROR: '+os.path.join(output_dir, 'screenshots')+' not writeable (check access)!\n')
-                sys.exit(1)
-
-    # check further screenshots dependencies
-    if screenshots is True and importlib.util.find_spec("pandas") is None:
-        print('\nERROR: the \'pandas\' package is required for running this script, please install.\n')
-        sys.exit(1)
-
-    if screenshots is True and importlib.util.find_spec("matplotlib") is None:
-        print('\nERROR: the \'matplotlib\' package is required for running this script, please install.\n')
-        sys.exit(1)
-
-    if screenshots is True:
-        if os.environ.get('FREESURFER_HOME') is None:
-            print('\nERROR: need to set the FREESURFER_HOME environment variable\n')
-            sys.exit(1)
-        else:
-            path_check = os.path.join(os.environ['FREESURFER_HOME'], 'FreeSurferColorLUT.txt')
-            if not os.path.isfile(path_check):
-                print('\nERROR: the \'FreeSurferColorLUT.txt\' file needs to be present in the FREESURFER_HOME directory.\n')
-                sys.exit(1)
-
-    # check screenshots_base
-    screenshots_base = [screenshots_base]
-
-    # check screenshots_overlay (this is either 'default' or 'none' or a single file or a list; further checks prior to execution of the screenshots module)
-    if screenshots_overlay.lower() == 'none':
-        screenshots_overlay = None
-        print("Found screenshot overlays set to None")
-    else:
-        screenshots_overlay = [screenshots_overlay]
-
-    # check screenshots_surf (this is either 'default' or 'none' or a single file or a list; further checks prior to execution of the screenshots module)
-    if not isinstance(screenshots_surf, list):
-        screenshots_surf = [screenshots_surf]
-    if screenshots_surf[0].lower() == 'none':
-        screenshots_surf = None
-        print("Found screenshot surfaces set to None")
-
-    # check if screenshots_views argument can be evaluated
-    if screenshots_views == 'default':
-        screenshots_views = [screenshots_views]
-    else:
-        for x in screenshots_views:
-            isXYZ = x.split("=")[0] == "x" or x.split("=")[0] == "y" or x.split("=")[0] == "z"
-            try:
-                int(x.split("=")[1])
-                isConvertible = True
-            except:
-                isConvertible = False
-            if not isXYZ or not isConvertible:
-                print()
-                print('ERROR: could not understand '+x)
-                print()
-                print('       the --screenshots_views argument can only contain one or more x=<numeric> y=<numeric> z=<numeric> expressions.')
-                print()
-                print('       for example: --screenshots_views x=0')
-                print('                    --screenshots_views x=-10 x=10 y=0')
-                print('                    --screenshots_views x=0 z=0')
-                print()
-                sys.exit(1)
-
-        print("Found screenshot coordinates ", screenshots_views)
-        screenshots_views = [ (y[0], int(y[1])) for y in [ x.split("=") for x in screenshots_views ] ]
-
-    # check screenshots_layout
-    if screenshots_layout is not None:
-        if all([ x.isdigit() for x in screenshots_layout ]):
-            screenshots_layout = [ int(x) for x in screenshots_layout ]
-        else:
-            print('ERROR: screenshots_layout argument can only contain integer numbers\n')
-            sys.exit(1)
-
-    # check if fornix subdirectory exists or can be created and is writable
-    if fornix is True:
-        if os.path.isdir(os.path.join(output_dir, 'fornix')):
-            print("Found fornix directory", os.path.join(output_dir, 'fornix'))
-        else:
-            try:
-                os.mkdir(os.path.join(output_dir, 'fornix'))
-            except:
-                print('ERROR: cannot create fornix directory '+os.path.join(output_dir, 'fornix')+'\n')
-                sys.exit(1)
-
-            try:
-                testfile = tempfile.TemporaryFile(dir=os.path.join(output_dir, 'fornix'))
-                testfile.close()
-            except OSError as e:
-                if e.errno != errno.EACCES:  # 13
-                    e.filename = os.path.join(output_dir, 'fornix')
-                    raise
-                print('\nERROR: '+os.path.join(output_dir, 'fornix')+' not writeable (check access)!\n')
-                sys.exit(1)
-
-    # check if hypothalamus subdirectory exists or can be created and is writable
-    if hypothalamus is True:
-        if os.path.isdir(os.path.join(output_dir, 'hypothalamus')):
-            print("Found hypothalamus directory", os.path.join(output_dir, 'hypothalamus'))
-        else:
-            try:
-                os.mkdir(os.path.join(output_dir, 'hypothalamus'))
-            except:
-                print('ERROR: cannot create hypothalamus directory '+os.path.join(output_dir, 'hypothalamus')+'\n')
-                sys.exit(1)
-
-            try:
-                testfile = tempfile.TemporaryFile(dir=os.path.join(output_dir, 'hypothalamus'))
-                testfile.close()
-            except OSError as e:
-                if e.errno != errno.EACCES:  # 13
-                    e.filename = os.path.join(output_dir, 'hypothalamus')
-                    raise
-                print('\nERROR: '+os.path.join(output_dir, 'hypothalamus')+' not writeable (check access)!\n')
-                sys.exit(1)
-
-    # check if shape subdirectory exists or can be created and is writable
-    if shape is True:
-        if os.path.isdir(os.path.join(output_dir, 'brainprint')):
-            print("Found brainprint directory", os.path.join(output_dir, 'brainprint'))
-        else:
-            try:
-                os.makedirs(os.path.join(output_dir, 'brainprint'))
-            except:
-                print('\nERROR: cannot create brainprint directory '+os.path.join(output_dir, 'brainprint')+'\n')
-                sys.exit(1)
-
-            try:
-                testfile = tempfile.TemporaryFile(dir=os.path.join(output_dir, 'brainprint'))
-                testfile.close()
-            except OSError as e:
-                if e.errno != errno.EACCES:  # 13
-                    e.filename = os.path.join(output_dir, 'brainprint')
-                    raise
-                print('\nERROR: '+os.path.join(output_dir, 'brainprint')+' not writeable (check access)!\n')
-                sys.exit(1)
-
-    # check if shapeDNA / brainPrint dependencies
-    if shape is True:
-        # check if brainprintpython can be imported
-        if  importlib.util.find_spec("brainprint") is None:
-            print("\nERROR: could not import the brainprint package, is it installed?")
-            sys.exit(1)
-
-        if  importlib.util.find_spec("lapy") is None:
-            print("\nERROR: could not import the lapy package, is it installed?")
-            sys.exit(1)
-
-    # check if outlier subdirectory exists or can be created and is writable
-    if outlier is True:
-        if os.path.isdir(os.path.join(output_dir, 'outliers')):
-            print("Found outliers directory", os.path.join(output_dir, 'outliers'))
-        else:
-            try:
-                os.makedirs(os.path.join(output_dir, 'outliers'))
-            except:
-                print('\nERROR: cannot create outliers directory '+os.path.join(output_dir, 'outliers')+'\n')
-                sys.exit(1)
-
-            try:
-                testfile = tempfile.TemporaryFile(dir=os.path.join(output_dir, 'outliers'))
-                testfile.close()
-            except OSError as e:
-                if e.errno != errno.EACCES:  # 13
-                    e.filename = os.path.join(output_dir, 'outliers')
-                    raise
-                print('\nERROR: '+os.path.join(output_dir, 'outliers')+' not writeable (check access)!\n')
-                sys.exit(1)
-
-    # check if outlier-table exists if it was given, otherwise exit
-    if outlier_table is not None:
-        if os.path.isfile(outlier_table):
-            print("Found table with normative values ", outlier_table)
-        else:
-            print("ERROR: Could not find table with normative values ", outlier_table)
-            sys.exit(1)
-
-    # check for required files
-    subjects_to_remove = list()
-    for subject in subjects:
-
-        # -files: stats/aseg.stats
-        path_check = os.path.join(subjects_dir, subject, "stats", "aseg.stats")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        # -files: surf/[lr]h.w-g.pct.mgh, label/[lr]h.cortex.label
-        path_check = os.path.join(subjects_dir, subject, "surf", "lh.w-g.pct.mgh")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        path_check = os.path.join(subjects_dir, subject, "surf", "rh.w-g.pct.mgh")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        path_check = os.path.join(subjects_dir, subject, "label", "lh.cortex.label")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        path_check = os.path.join(subjects_dir, subject, "label", "rh.cortex.label")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        # -files: mri/transforms/talairach.lta
-        path_check = os.path.join(subjects_dir, subject, "mri", "transforms", "talairach.lta")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        # -files: mri/norm.mgz, mri/aseg.mgz, mri/aparc+aseg.mgz for FreeSurfer
-        # -files: mri/norm.mgz, mri/aseg.mgz, mri/aparc.DKTatlas+aseg.deep.mgz for FastSurfer
-        path_check = os.path.join(subjects_dir, subject, "mri", "norm.mgz")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        path_check = os.path.join(subjects_dir, subject, "mri", "aseg.mgz")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        if fastsurfer is True:
-            path_check = os.path.join(subjects_dir, subject, "mri", "aparc.DKTatlas+aseg.deep.mgz")
-        else:
-            path_check = os.path.join(subjects_dir, subject, "mri", "aparc+aseg.mgz")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        # -files: scripts/recon-all.log
-        path_check = os.path.join(subjects_dir, subject, "scripts", "recon-all.log")
-        if not os.path.isfile(path_check):
-            print("Could not find", path_check, "for subject", subject)
-            subjects_to_remove.extend([subject])
-
-        # check screenshots
-        if screenshots is True and screenshots_surf==["default"]:
-
-            # -files: surf/[lr]h.white (optional), surf/[lr]h.pial (optional)
-            path_check = os.path.join(subjects_dir, subject, "surf", "lh.white")
-            if not os.path.isfile(path_check):
-                print("Could not find", path_check, "for subject", subject)
-                subjects_to_remove.extend([subject])
-
-            path_check = os.path.join(subjects_dir, subject, "surf", "rh.white")
-            if not os.path.isfile(path_check):
-                print("Could not find", path_check, "for subject", subject)
-                subjects_to_remove.extend([subject])
-
-            path_check = os.path.join(subjects_dir, subject, "surf", "lh.pial")
-            if not os.path.isfile(path_check):
-                print("Could not find", path_check, "for subject", subject)
-                subjects_to_remove.extend([subject])
-
-            path_check = os.path.join(subjects_dir, subject, "surf", "rh.pial")
-            if not os.path.isfile(path_check):
-                print("Could not find", path_check, "for subject", subject)
-                subjects_to_remove.extend([subject])
-
-        # check fornix
-        if fornix is True:
-
-            # -files: mri/transforms/cc_up.lta
-            path_check = os.path.join(subjects_dir, subject, "mri", "transforms", "cc_up.lta")
-            if not os.path.isfile(path_check):
-                print("Could not find", path_check, "for subject", subject)
-                subjects_to_remove.extend([subject])
-
-    # remove subjects with missing files after creating unique list
-    [ subjects.remove(x) for x in list(set(subjects_to_remove)) ]
-
-    # check if we have any subjects after all
-    if not subjects:
-        print("\nERROR: no subjects to process")
-        sys.exit(1)
-
-    # now return
-    return subjects_dir, output_dir, subjects, subjects_file, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer
-
-
-# ------------------------------------------------------------------------------
-# check packages
-
-def _check_packages():
-    """
-    an internal function to check required / recommended packages
-
-    """
-
-    import os
-    import sys
-    import importlib.util
-
-    if os.environ.get('FREESURFER_HOME') is None:
-        print('\nERROR: need to set the FREESURFER_HOME environment variable\n')
-        sys.exit(1)
-
-    if sys.version_info <= (3, 5):
-        print('\nERROR: Python version must be 3.5 or greater\n')
-        sys.exit(1)
-
-    if importlib.util.find_spec("skimage") is None:
-        print('\nERROR: the \'skimage\' package is required for running this script, please install.\n')
-        sys.exit(1)
-
-    if importlib.util.find_spec("nibabel") is None:
-        print('\nERROR: the \'nibabel\' package is required for running this script, please install.\n')
-        sys.exit(1)
-
-    if importlib.util.find_spec("transforms3d") is None:
-        # this package is less important and less standard, so we just return a
-        # warning (and NaNs) if it is not found.
-        print('\nWARNING: the \'transforms3d\' package is recommended, please install.\n')
-
-
-# ------------------------------------------------------------------------------
-# do qatools
-
-def _do_qatools(subjects_dir, output_dir, subjects, shape=False, screenshots=False, screenshots_html=False, screenshots_base="default", screenshots_overlay=["default"], screenshots_surf=["default"], screenshots_views=["default"], screenshots_layout=None, fornix=False, fornix_html=False, hypothalamus=False, hypothalamus_html=False, outlier=False, outlier_table=None, fastsurfer=False):
-    """
-    an internal function to run the qatools submodules
-
-    """
-
-    # ------------------------------------------------------------------------------
-    # imports
-
-    import os
-    import csv
-    import time
-
-    import numpy as np
-
-    from qatoolspython.checkSNR import checkSNR
-    from qatoolspython.checkCCSize import checkCCSize
-    from qatoolspython.checkTopology import checkTopology
-    from qatoolspython.checkContrast import checkContrast
-    from qatoolspython.checkRotation import checkRotation
-    from qatoolspython.evaluateFornixSegmentation import evaluateFornixSegmentation
-    from qatoolspython.evaluateHypothalamicSegmentation import evaluateHypothalamicSegmentation
-    from qatoolspython.createScreenshots import createScreenshots
-    from qatoolspython.outlierDetection import outlierTable
-    from qatoolspython.outlierDetection import outlierDetection
-
-    # ------------------------------------------------------------------------------
-    # internal settings (might be turned into command-line arguments in the future)
-
-    SNR_AMOUT_EROSION = 3
-    FORNIX_SCREENSHOT = True
-    FORNIX_SHAPE = False
-    FORNIX_N_EIGEN = 15
-    HYPOTHALAMUS_SCREENSHOT = True
-    OUTLIER_N_MIN = 5
-
-    SHAPE_EVEC = False
-    SHAPE_SKIPCORTEX = False
-    SHAPE_NUM = 50
-    SHAPE_NORM = "geometry"
-    SHAPE_REWEIGHT = True
-    SHAPE_ASYMMETRY = True
-
-    # --------------------------------------------------------------------------
-    # process
-
-    # start the processing with a message
-    print("")
-    print("-----------------------------")
-
-    # create metrics dict
-    metricsDict = dict()
-
-    # create images dict
-    imagesScreenshotsDict = dict()
-    imagesFornixDict = dict()
-    imagesHypothalamusDict = dict()
-
-    # create status dict
-    statusDict = dict()
-
-    # loop through the specified subjects
-    for subject in subjects:
-
-        #
-        print("Starting qatools-python for subject", subject, "at", time.strftime('%Y-%m-%d %H:%M %Z', time.localtime(time.time())))
-        print("")
-
-        # ----------------------------------------------------------------------
-        # set images
-
-        if fastsurfer is True:
-            aparc_image = "aparc.DKTatlas+aseg.deep.mgz"
-        else:
-            aparc_image = "aparc+aseg.mgz"
-
-        # ----------------------------------------------------------------------
-        # add subject to dictionary
-
-        metricsDict.update( { subject : { 'subject' : subject } } )
-        statusDict.update( { subject : { 'subject' : subject } } )
-
-        # ----------------------------------------------------------------------
-        # compute core metrics
-
-        # set status
-        metrics_ok = True
-
-        # get WM and GM SNR for orig.mgz
-        try:
-            wm_snr_orig, gm_snr_orig = checkSNR(subjects_dir, subject, SNR_AMOUT_EROSION, ref_image="orig.mgz", aparc_image=aparc_image)
-
-        except:
-            wm_snr_orig = np.nan
-            gm_snr_orig = np.nan
-            metrics_ok = False
-
-        # get WM and GM SNR for norm.mgz
-        try:
-            wm_snr_norm, gm_snr_norm = checkSNR(subjects_dir, subject, SNR_AMOUT_EROSION, ref_image="norm.mgz", aparc_image=aparc_image)
-
-        except:
-            wm_snr_norm = np.nan
-            gm_snr_norm = np.nan
-            metrics_ok = False
-
-        # check CC size
-        try:
-            cc_size = checkCCSize(subjects_dir, subject)
-
-        except:
-            cc_size = np.nan
-            metrics_ok = False
-
-        # check topology
-        try:
-            holes_lh, holes_rh, defects_lh, defects_rh, topo_lh, topo_rh = checkTopology(subjects_dir, subject)
-
-        except:
-            holes_lh = np.nan
-            holes_rh = np.nan
-            defects_lh = np.nan
-            defects_rh = np.nan
-            topo_lh = np.nan
-            topo_rh = np.nan
-            metrics_ok = False
-
-        # check contrast
-        try:
-            con_snr_lh, con_snr_rh = checkContrast(subjects_dir, subject)
-
-        except:
-            con_snr_lh = np.nan
-            con_snr_rh = np.nan
-            metrics_ok = False
-
-        # check rotation
-        try:
-            rot_tal_x, rot_tal_y, rot_tal_z = checkRotation(subjects_dir, subject)
-
-        except:
-            rot_tal_x = np.nan
-            rot_tal_y = np.nan
-            rot_tal_z = np.nan
-            metrics_ok = False
-
-        # store data
-        metricsDict[subject].update({
-            'wm_snr_orig': wm_snr_orig, 'gm_snr_orig' : gm_snr_orig,
-            'wm_snr_norm' : wm_snr_norm, 'gm_snr_norm' : gm_snr_norm,
-            'cc_size' : cc_size,
-            'holes_lh' : holes_lh, 'holes_rh' : holes_rh, 'defects_lh' : defects_lh, 'defects_rh' : defects_rh, 'topo_lh' : topo_lh, 'topo_rh' : topo_rh,
-            'con_snr_lh' : con_snr_lh, 'con_snr_rh' : con_snr_rh,
-            'rot_tal_x' : rot_tal_x, 'rot_tal_y' : rot_tal_y , 'rot_tal_z' : rot_tal_z
-            })
-
-        # store data
-        statusDict[subject].update( { 'metrics' : metrics_ok } )
-
-        #
-        print("")
-
-        # ----------------------------------------------------------------------
-        # run optional modules: shape analysis
-
-        if shape is True:
-
-            #
-            try:
-
-                # message
-                print("-----------------------------")
-                print("Running brainPrint analysis ...")
-                print("")
-
-                # compute brainprint (will also compute shapeDNA)
-                from brainprint import brainprint
-
-                # check / create subject-specific brainprint_outdir
-                brainprint_outdir = os.path.join(output_dir, 'brainprint', subject)
-
-                # run brainPrint
-                evMat, evecMat, dstMat = brainprint.run_brainprint(sdir=subjects_dir, sid=subject, outdir=brainprint_outdir, evec=SHAPE_EVEC, skipcortex=SHAPE_SKIPCORTEX, num=SHAPE_NUM, norm=SHAPE_NORM, reweight=SHAPE_REWEIGHT, asymmetry=SHAPE_ASYMMETRY)
-
-                # get a subset of the brainprint results
-                distDict = { subject : dstMat }
-
-                # return
-                shape_ok = True
-
-                # check / create subject-specific brainprint_outdir
-                brainprint_outdir = os.path.join(output_dir, 'brainprint', subject)
-
-                # run brainPrint
-                evMat, evecMat, dstMat = brainprint.run_brainprint(sdir=subjects_dir, sid=subject, outdir=brainprint_outdir, evec=SHAPE_EVEC, skipcortex=SHAPE_SKIPCORTEX, num=SHAPE_NUM, norm=SHAPE_NORM, reweight=SHAPE_REWEIGHT, asymmetry=SHAPE_ASYMMETRY)
-
-                # get a subset of the brainprint results
-                distDict = { subject : dstMat }
-
-                # return
-                shape_ok = True
-
-            #
-            except:
-
-                distDict = { subject : [] }
-                print("ERROR: the shape module failed for subject "+subject)
-                shape_ok = False
-
-            # store data
-            metricsDict[subject].update(distDict[subject])
-
-            # store data
-            statusDict[subject].update( { 'shape' : shape_ok } )
-
-        # ----------------------------------------------------------------------
-        # run optional modules: screenshots
-
-        if screenshots is True:
-
-            #
-            try:
-
-                # message
-                print("-----------------------------")
-                print("Creating screenshots ...")
-                print("")
-
-                # check / create subject-specific screenshots_outdir
-                screenshots_outdir = os.path.join(output_dir, 'screenshots', subject)
-                if not os.path.isdir(screenshots_outdir):
-                    os.mkdir(screenshots_outdir)
-                outfile = os.path.join(screenshots_outdir, subject+'.png')
-
-                # re-initialize
-                screenshots_base_subj = list()
-                screenshots_overlay_subj = list()
-                screenshots_surf_subj = list()
-
-                # check screenshots_base
-                if screenshots_base[0] == "default":
-                    screenshots_base_subj = screenshots_base
-                    print("Using default for screenshot base image")
-                elif os.path.isfile(screenshots_base[0]):
-                    screenshots_base_subj = screenshots_base
-                    print("Using "+screenshots_base_subj[0]+" as screenshot base image")
-                elif os.path.isfile(os.path.join(subjects_dir, subject, 'mri', screenshots_base[0])):
-                    screenshots_base_subj = [os.path.join(subjects_dir, subject, 'mri', screenshots_base[0])]
-                    print("Using "+screenshots_base_subj[0]+" as screenshot base image")
-                else:
-                    print('\nERROR: cannot find the screenshots base file '+screenshots_base[0]+'\n')
-                    sys.exit(1)
-
-                # check screenshots_overlay
-                if screenshots_overlay is not None:
-                    if screenshots_overlay[0] == "default":
-                        screenshots_overlay_subj = screenshots_overlay
-                        print("Using default for screenshot overlay image")
-                    elif os.path.isfile(screenshots_overlay[0]):
-                        screenshots_overlay_subj = screenshots_overlay
-                        print("Using "+screenshots_overlay_subj[0]+" as screenshot overlay image")
-                    elif os.path.isfile(os.path.join(subjects_dir, subject, 'mri', screenshots_overlay[0])):
-                        screenshots_overlay_subj = [os.path.join(subjects_dir, subject, 'mri', screenshots_overlay[0])]
-                        print("Using "+screenshots_overlay_subj[0]+" as screenshot overlay image")
-                    else:
-                        print('\nERROR: cannot find the screenshots overlay file '+screenshots_overlay[0]+'\n')
-                        sys.exit(1)
-                else:
-                    screenshots_overlay_subj = screenshots_overlay
-
-                # check screenshots_surf
-                if screenshots_surf is not None:
-                    for screenshots_surf_i in screenshots_surf:
-                        if screenshots_surf_i == "default":
-                            print("Using default for screenshot surface")
-                        elif os.path.isfile(screenshots_surf_i):
-                            print("Using "+screenshots_surf_i+" as screenshot surface")
-                        elif os.path.isfile(os.path.join(subjects_dir, subject, 'surf', screenshots_surf_i)):
-                            screenshots_surf_i = os.path.join(subjects_dir, subject, 'surf', screenshots_surf_i)
-                            print("Using "+screenshots_surf_i+" as screenshot surface")
-                        else:
-                            print('\nERROR: cannot find the screenshots surface file '+screenshots_surf_i+'\n')
-                            sys.exit(1)
-                        screenshots_surf_subj.append(screenshots_surf_i)
-                else:
-                    screenshots_surf_subj = None
-
-                # process
-                createScreenshots(SUBJECT=subject, SUBJECTS_DIR=subjects_dir, OUTFILE=outfile, INTERACTIVE=False, BASE=screenshots_base_subj, OVERLAY=screenshots_overlay_subj, SURF=screenshots_surf_subj, VIEWS=screenshots_views, LAYOUT=screenshots_layout)
-
-                # return
-                screenshots_ok = True
-
-            #
-            except:
-
-                print("ERROR: screenshots module failed for subject "+subject)
-                screenshots_ok = False
-
-            # store data
-            imagesScreenshotsDict[subject] = outfile
-
-            # store data
-            statusDict[subject].update( { 'screenshots' : screenshots_ok } )
-
-        # ----------------------------------------------------------------------
-        # run optional modules: fornix
-
-        if fornix is True:
-
-            #
-            try:
-
-                # message
-                print("-----------------------------")
-                print("Checking fornix segmentation ...")
-                print("")
-
-                # check / create subject-specific fornix_outdir
-                fornix_outdir = os.path.join(output_dir, 'fornix', subject)
-                if not os.path.isdir(fornix_outdir):
-                    os.mkdir(fornix_outdir)
-                fornix_screenshot_outfile = os.path.join(fornix_outdir, "cc.png")
-
-                # process
-                fornixShapeOutput = evaluateFornixSegmentation(SUBJECT=subject, SUBJECTS_DIR=subjects_dir, OUTPUT_DIR=fornix_outdir, CREATE_SCREENSHOT=FORNIX_SCREENSHOT, SCREENSHOTS_OUTFILE=fornix_screenshot_outfile, RUN_SHAPEDNA=FORNIX_SHAPE, N_EIGEN=FORNIX_N_EIGEN)
-
-                # create a dictionary from fornix shape ouput
-                fornixShapeDict = { subject : dict(zip(map("fornixShapeEV{:0>3}".format, range(FORNIX_N_EIGEN)), fornixShapeOutput)) }
-
-                # return
-                fornix_ok = True
-
-            #
-            except:
-
-                fornixShapeDict = { subject : dict(zip(map("fornixShapeEV{:0>3}".format, range(FORNIX_N_EIGEN)), np.full(FORNIX_N_EIGEN, np.nan))) }
-                print("ERROR: fornix module failed for subject "+subject)
-                fornix_ok = False
-
-            # store data
-            if FORNIX_SHAPE:
-                metricsDict[subject].update(fornixShapeDict[subject])
-
-            # store data
-            if FORNIX_SCREENSHOT:
-                imagesFornixDict[subject] = fornix_screenshot_outfile
-            else:
-                imagesFornixDict[subject] = []
-
-            # store data
-            statusDict[subject].update( { 'fornix' : fornix_ok } )
-
-        # ----------------------------------------------------------------------
-        # run optional modules: hypothalamus
-
-        if hypothalamus is True:
-
-            #
-            try:
-
-                # message
-                print("-----------------------------")
-                print("Checking hypothalamus segmentation ...")
-                print("")
-
-                # check / create subject-specific hypothalamus_outdir
-                hypothalamus_outdir = os.path.join(output_dir, 'hypothalamus', subject)
-                if not os.path.isdir(hypothalamus_outdir):
-                    os.mkdir(hypothalamus_outdir)
-                hypothalamus_screenshot_outfile = os.path.join(hypothalamus_outdir, "hypothalamus.png")
-
-                # process
-                evaluateHypothalamicSegmentation(SUBJECT=subject, SUBJECTS_DIR=subjects_dir, OUTPUT_DIR=hypothalamus_outdir, CREATE_SCREENSHOT=HYPOTHALAMUS_SCREENSHOT, SCREENSHOTS_OUTFILE=hypothalamus_screenshot_outfile)
-
-                # return
-                hypothalamus_ok = True
-
-            #
-            except:
-
-                print("ERROR: hypothalamus module failed for subject "+subject)
-                hypothalamus_ok = False
-
-            # store data
-            if HYPOTHALAMUS_SCREENSHOT:
-                imagesHypothalamusDict[subject] = hypothalamus_screenshot_outfile
-            else:
-                imagesHypothalamusDict[subject] = []
-
-            # store data
-            statusDict[subject].update( { 'hypothalamus' : hypothalamus_ok } )
-
-        # --------------------------------------------------------------------------
-        # message
-        print("Finished subject", subject, "at", time.strftime('%Y-%m-%d %H:%M %Z', time.localtime(time.time())))
-        print("")
-
-    # --------------------------------------------------------------------------
-    # run optional modules: outlier detection
-
-    if outlier is True:
-
-        #
-        try:
-
-            # message
-            print("---------------------------------------")
-            print("Running outlier detection module ...")
-            print("")
-
-            # determine outlier-table and get data
-            if outlier_table is None:
-                outlierDict = outlierTable()
-            else:
-                outlierDict = dict()
-                with open(outlier_table, newline='') as csvfile:
-                    outlierCsv = csv.DictReader(csvfile, delimiter=',')
-                    for row in outlierCsv:
-                        outlierDict.update({row['label']: {'lower': float(row['lower']), 'upper': float(row['upper'])}})
-
-            # process
-            outlier_outdir = os.path.join(output_dir, 'outliers')
-            n_outlier_sample_nonpar, n_outlier_sample_param, n_outlier_norms = outlierDetection(subjects, subjects_dir, outlier_outdir, outlierDict, min_no_subjects=OUTLIER_N_MIN)
-
-            # create a dictionary from outlier module ouput
-            outlierDict = dict()
-            for subject in subjects:
-                outlierDict.update({subject : {
-                    'n_outlier_sample_nonpar' : n_outlier_sample_nonpar[subject],
-                    'n_outlier_sample_param': n_outlier_sample_param[subject],
-                    'n_outlier_norms': n_outlier_norms[subject]
-                    }
-                })
-
-            # return
-            outlier_ok = True
-
-        #
-        except:
-
-            # create a dictionary from outlier module ouput
-            outlierDict = dict()
-            for subject in subjects:
-                outlierDict.update({subject : {
-                    'n_outlier_sample_nonpar' : np.nan,
-                    'n_outlier_sample_param': np.nan,
-                    'n_outlier_norms': np.nan
-                    }
-                })
-
-            print("ERROR: outlier module failed")
-            outlier_ok = False
-
-        # store data
-        for subject in subjects:
-            metricsDict[subject].update(outlierDict[subject])
-
-        # message
-        print("Done")
-        print("")
-
-    # --------------------------------------------------------------------------
-    # generate output
-
-    metricsFieldnames = ['subject']
-
-    # we pre-specify the fieldnames because we want to have this particular order
-    metricsFieldnames.extend(['wm_snr_orig', 'gm_snr_orig', 'wm_snr_norm', 'gm_snr_norm', 'cc_size', 'holes_lh', 'holes_rh', 'defects_lh', 'defects_rh', 'topo_lh', 'topo_rh', 'con_snr_lh', 'con_snr_rh', 'rot_tal_x', 'rot_tal_y', 'rot_tal_z'])
-
-    # collect other keys; need to iterate over subjects, because not all of them
-    # necessarily have the same set of keys
-    if shape is True:
-        shapeKeys = list()
-        for subject in distDict.keys():
-            if len(distDict[subject])>0:
-                shapeKeys = list(np.unique(shapeKeys + list(distDict[subject].keys())))
-        metricsFieldnames.extend(shapeKeys)
-
-    if fornix is True and FORNIX_SHAPE is True:
-        fornixKeys = list()
-        for subject in fornixShapeDict.keys():
-            if len(fornixShapeDict[subject])>0:
-                fornixKeys = list(np.unique(fornixKeys + list(fornixShapeDict[subject].keys())))
-        metricsFieldnames.extend(sorted(fornixKeys))
-
-    if outlier is True:
-        outlierKeys = list()
-        for subject in outlierDict.keys():
-            if len(outlierDict[subject])>0:
-                outlierKeys = list(np.unique(outlierKeys + list(outlierDict[subject].keys())))
-        metricsFieldnames.extend(sorted(outlierKeys))
-
-    # determine output file names
-    path_data_file = os.path.join(output_dir, 'qatools-results.csv')
-    path_html_file = os.path.join(output_dir, 'qatools-results.html')
-
-    # write csv
-    with open(path_data_file, 'w') as datafile:
-        csvwriter = csv.DictWriter(datafile, fieldnames=metricsFieldnames, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
-        csvwriter.writeheader()
-        for subject in sorted(list(metricsDict.keys())):
-            csvwriter.writerow(metricsDict[subject])
-
-    # generate html output
-    if (screenshots is True and screenshots_html is True) or (fornix is True and fornix_html is True) or (hypothalamus is True and hypothalamus_html is True):
-        with open(path_html_file, 'w') as htmlfile:
-            print("<html>", file=htmlfile)
-            print("<head>", file=htmlfile)
-            print("<title>FreeSurfer QAtools screenshots</title>", file=htmlfile)
-            print("</head>", file=htmlfile)
-            print("<style> body, h1, h2, h3, h4, h5, h6  { font-family: Arial, Helvetica, sans-serif ; } </style>)", file=htmlfile)
-            print("<body style=\"background-color:Black\">", file=htmlfile)
-
-            # screenshots
-            if screenshots is True and screenshots_html is True:
-                print("<h1 style=\"color:white\">Screenshots</h1>", file=htmlfile)
-                for subject in sorted(list(imagesScreenshotsDict.keys())):
-                    print("<h2 style=\"color:white\">Subject "+subject+"</h2>", file=htmlfile)
-                    print("<p><a href=\""+os.path.join('screenshots', subject, os.path.basename(imagesScreenshotsDict[subject]))+"\">"
-                        +"<img src=\""+os.path.join('screenshots', subject, os.path.basename(imagesScreenshotsDict[subject]))+"\" "
-                        +"alt=\"Image for subject "+subject+"\" style=\"width:75vw;min_width:200px;\"></img></a></p>", file=htmlfile)
-            # fornix
-            if fornix is True and fornix_html is True:
-                print("<h1 style=\"color:white\">Fornix</h1>", file=htmlfile)
-                for subject in sorted(list(imagesFornixDict.keys())):
-                    print("<h2 style=\"color:white\">Subject "+subject+"</h2>", file=htmlfile)
-                    print("<p><a href=\""+os.path.join('fornix', subject, os.path.basename(imagesFornixDict[subject]))+"\">"
-                        +"<img src=\""+os.path.join('fornix', subject, os.path.basename(imagesFornixDict[subject]))+"\" "
-                        +"alt=\"Image for subject "+subject+"\" style=\"width:75vw;min_width:200px;\"></img></a></p>", file=htmlfile)
-
-            # hypothalamus
-            if hypothalamus is True and hypothalamus_html is True:
-                print("<h1 style=\"color:white\">Hypothalamus</h1>", file=htmlfile)
-                for subject in sorted(list(imagesHypothalamusDict.keys())):
-                    print("<h2 style=\"color:white\">Subject "+subject+"</h2>", file=htmlfile)
-                    print("<p><a href=\""+os.path.join('hypothalamus', subject, os.path.basename(imagesHypothalamusDict[subject]))+"\">"
-                        +"<img src=\""+os.path.join('hypothalamus', subject, os.path.basename(imagesHypothalamusDict[subject]))+"\" "
-                        +"alt=\"Image for subject "+subject+"\" style=\"width:75vw;min_width:200px;\"></img></a></p>", file=htmlfile)
-
-            #
-            print("</body>", file=htmlfile)
-            print("</html>", file=htmlfile)
-
-
-
-
-# ------------------------------------------------------------------------------
-# run qatools
-
-def run_qatools(subjects_dir, output_dir, subjects=None, subjects_file=None, shape=False, screenshots=False, screenshots_html=False, screenshots_base="default", screenshots_overlay="default", screenshots_surf="default", screenshots_views="default", screenshots_layout=None, fornix=False, fornix_html=False, hypothalamus=False, hypothalamus_html=False, outlier=False, outlier_table=None, fastsurfer=False):
-    """
-    a function to run the qatools submodules
-
-    """
-
-    # ------------------------------------------------------------------------------
-    #
-
-    # check arguments
-    subjects_dir, output_dir, subjects, subjects_file, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer = _check_arguments(subjects_dir, output_dir, subjects, subjects_file, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer)
-
-    # check packages
-    _check_packages()
-
-    # run qatools
-    _do_qatools(subjects_dir, output_dir, subjects, shape, screenshots, screenshots_html, screenshots_base, screenshots_overlay, screenshots_surf, screenshots_views, screenshots_layout, fornix, fornix_html, hypothalamus, hypothalamus_html, outlier, outlier_table, fastsurfer)
diff --git a/qatoolspython/qatoolspythonUtils.py b/qatoolspython/qatoolspythonUtils.py
deleted file mode 100644
index 8efe790..0000000
--- a/qatoolspython/qatoolspythonUtils.py
+++ /dev/null
@@ -1,185 +0,0 @@
-"""
-This module provides various import/export functions as well as the 
-'levelsetsTria' function
-
-"""
-
-# ------------------------------------------------------------------------------
-
-def importMGH(filename):
-    """
-    A function to read Freesurfer MGH files.
-
-    Required arguments:
-        - filename
-
-    Returns:
-        - vol
-
-    Requires valid mgh file. If not found, NaNs will be returned.
-
-    """
-
-    import os
-    import struct
-    import numpy
-
-    if not os.path.exists(filename):
-        print("WARNING: could not find "+filename+", returning NaNs")
-        return numpy.nan
-
-    fp = open(filename,'rb')
-    intsize = struct.calcsize('>i')
-    shortsize = struct.calcsize('>h')
-    floatsize = struct.calcsize('>f')
-    charsize = struct.calcsize('>b')
-
-    v = struct.unpack('>i',fp.read(intsize))[0]
-    ndim1 = struct.unpack('>i',fp.read(intsize))[0]
-    ndim2 = struct.unpack('>i',fp.read(intsize))[0]
-    ndim3 = struct.unpack('>i',fp.read(intsize))[0]
-    nframes = struct.unpack('>i',fp.read(intsize))[0]
-    vtype = struct.unpack('>i',fp.read(intsize))[0]
-    dof = struct.unpack('>i',fp.read(intsize))[0]
-
-    UNUSED_SPACE_SIZE = 256
-    USED_SPACE_SIZE = (3*4) + (4*3*4) # space for ras transform
-    unused_space_size = UNUSED_SPACE_SIZE - 2
-
-    ras_good_flag = struct.unpack('>h',fp.read(shortsize))[0]
-    if ras_good_flag:
-        # We read these in but don't process them
-        # as we just want to move to the volume data
-        delta = struct.unpack('>fff',fp.read(floatsize*3))
-        Mdc = struct.unpack('>fffffffff',fp.read(floatsize*9))
-        Pxyz_c = struct.unpack('>fff',fp.read(floatsize*3))
-
-    unused_space_size = unused_space_size - USED_SPACE_SIZE
-
-    for i in range(unused_space_size):
-        struct.unpack('>b',fp.read(charsize))[0]
-
-    nv = ndim1 * ndim2 * ndim3 * nframes
-    vol = numpy.fromstring(fp.read(floatsize*nv),dtype=numpy.float32).byteswap()
-
-    nvert = max([ndim1,ndim2,ndim3])
-    vol = numpy.reshape(vol,(ndim1,ndim2,ndim3,nframes),order='F')
-    vol = numpy.squeeze(vol)
-    fp.close()
-
-    return vol
-
-
-# ------------------------------------------------------------------------------
-
-def levelsetsTria(v, t, p, levelsets):
-    """
-    This is the levelsetsTria function
-
-    """
-
-    import numpy as np
-    from scipy.sparse import csr_matrix, lil_matrix
-
-    vLVL = list()
-    lLVL = list()
-    iLVL = list()
-
-    levelsets = (np.array(levelsets, ndmin=2))
-
-    for l in range(len(levelsets)):
-
-        A = lil_matrix((np.shape(v)[0],np.shape(v)[0]))
-
-        lvl = levelsets[l]
-
-        nlvl = p[t] > lvl
-
-        n = np.where(np.logical_or(np.sum(nlvl, axis=1) == 1 , np.sum(nlvl, axis=1) == 2))[0]
-
-        # interpolate points
-
-        ti = list()
-        vi = list()
-
-        for i in range(len(n)):
-
-            # which are the outlying points in the current tria?
-            oi = np.where(nlvl[n[i],:])[0]
-
-            #  convert 2 --> 1
-            if len(oi) == 2:
-                oi = np.setdiff1d((0,1,2), oi)
-
-            # find the two non - outyling points
-            oix = np.setdiff1d((0, 1, 2), oi)
-
-            # check if we have interpolated for one or both of these points before
-
-            if np.count_nonzero(A[t[n[i], oi], t[n[i], oix[0]]]) == 0:
-
-                # compute difference vectors between outlying point and other points
-
-                d10 = v[ t[n[i], oix[0]], :] - v[ t[n[i], oi], :]
-
-                # compute differences of all points to lvl to get interpolation factors
-
-                s10 = (lvl - p[t[n[i], oi]]) / (p[t[n[i], oix[0]]] - p[t[n[i], oi]])
-
-                # compute new points
-
-                v10 = s10 * d10 + v[ t[n[i], oi], :]
-
-                # update vi and index(order matters)
-
-                vi.append(v10.tolist()[0])
-
-                ti10 = len(vi)
-
-                # store between which two points we are interpolating (to avoid having duplicate points)
-
-                A[ t[n[i], oi], t[n[i], oix[0]] ] = ti10
-                A[ t[n[i], oix[0]], t[n[i], oi] ] = ti10
-
-            else:
-
-                ti10 = int(A[ t[n[i], oi], t[n[i], oix[0]] ].toarray().item())
-
-            # essentially the same as above, just for oix[1]
-
-            if np.count_nonzero(A[t[n[i], oi], t[n[i], oix[1]]]) == 0:
-
-                d20 = v[ t[n[i], oix[1]], :] - v[ t[n[i], oi], :]
-
-                s20 = (lvl - p[t[n[i], oi]]) / (p[t[n[i], oix[1]]] - p[t[n[i], oi]])
-
-                v20 = s20 * d20 + v[ t[n[i], oi], :]
-
-                # update vi and index(order matters)
-
-                vi.append(v20.tolist()[0])
-
-                ti20 = len(vi)
-
-                A[ t[n[i], oi], t[n[i], oix[1]] ] = ti20
-                A[ t[n[i], oix[1]], t[n[i], oi] ] = ti20
-
-            else:
-
-                ti20 = int(A[ t[n[i], oi], t[n[i], oix[1]] ].toarray().item())
-
-            # store new indices
-
-            ti.append((ti10,ti20))
-
-            # clean up
-
-            # clear oi oix d10 d20 s10 s20 v10 v20 t10 t20
-
-        # store
-
-        vLVL.append(vi)
-        lLVL.append(ti)
-        iLVL.append(n)
-
-    return vLVL, lLVL, iLVL
diff --git a/requirements.txt b/requirements.txt
index a90f103..66e81d3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,43 +1,10 @@
-#
-# This file is autogenerated by pip-compile with python 3.8
-# To update, run:
-#
-#    pip-compile
-#
-cycler==0.11.0
-    # via matplotlib
-kiwisolver==1.3.2
-    # via matplotlib
-matplotlib==3.4.3
-    # via qatoolspython (setup.py)
-nibabel==3.2.1
-    # via qatoolspython (setup.py)
-numpy==1.22.0
-    # via
-    #   matplotlib
-    #   nibabel
-    #   pandas
-    #   qatoolspython (setup.py)
-    #   scipy
-packaging==21.2
-    # via nibabel
-pandas==1.3.4
-    # via qatoolspython (setup.py)
-pillow==9.0.1
-    # via matplotlib
-pyparsing==2.4.7
-    # via
-    #   matplotlib
-    #   packaging
-python-dateutil==2.8.2
-    # via
-    #   matplotlib
-    #   pandas
-pytz==2021.3
-    # via pandas
-scipy==1.7.2
-    # via qatoolspython (setup.py)
-six==1.16.0
-    # via python-dateutil
-transforms3d==0.3.1
-    # via qatoolspython (setup.py)
+brainprint>=0.4.0
+lapy>=1.0.0
+kaleido
+matplotlib
+nibabel
+numpy
+pandas
+scipy
+scikit-image
+transforms3d
diff --git a/run_fsqc b/run_fsqc
new file mode 100755
index 0000000..af4f400
--- /dev/null
+++ b/run_fsqc
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+
+import fsqc
+
+from fsqc.fsqcMain import _parse_arguments
+
+if __name__ == "__main__":
+
+    # say hello
+    print("")
+    print("-----------------------------")
+    print("fsqc")
+    print("-----------------------------")
+    print("")
+
+    # parse arguments
+    argsDict = _parse_arguments()
+
+    # run fsqc
+    if argsDict is not None:
+        fsqc.run_fsqc(subjects_dir=None, output_dir=None, argsDict=argsDict)
diff --git a/setup.py b/setup.py
index f8a3c11..7223568 100644
--- a/setup.py
+++ b/setup.py
@@ -1,35 +1,9 @@
-from setuptools import setup, find_packages
-from os import path
+# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
+# If compatibility with legacy builds or versions of tools that don’t support
+# certain packaging standards (e.g. PEP 517 or PEP 660), a simple setup.py
+# script can be added to your project [1] (while keeping the configuration in
+# pyproject.toml):
 
-with open("README.md", "r") as fh:
-    long_description = fh.read()
+from setuptools import setup
 
-# see: https://packaging.python.org/guides/distributing-packages-using-setuptools/#setup-py for a description
-# see: https://github.com/pypa/sampleproject/blob/master/setup.py for an example
-# see: https://pip.pypa.io/en/stable/reference/pip_install for a description how to use pip install in conjunction with a git repository
-
-setup(
-    name="qatoolspython",
-    version="1.3.0",
-    author="Kersten Diers, Martin Reuter, and others (see README)",
-    description="A set of quality assurance / quality control scripts for Freesurfer-processed structural MRI data",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    url="https://github.com/reuter-lab/qatools-python",
-    packages=['qatoolspython'],
-    # see https://pypi.org/classifiers/
-    classifiers=[
-        "Programming Language :: Python :: 3",
-        "Development Status :: 3 - Alpha",
-        "License :: OSI Approved :: MIT",
-#        "Operating System :: OS Independent",
-    ],
-    python_requires='>=3.5',
-    keywords='Freesurfer',
-    # This field lists other packages that your project depends on to run.
-    # Any package you put here will be installed by pip when your project is
-    # installed, so they must be valid existing projects.
-    # For an analysis of "install_requires" vs pip's requirements files see:
-    # https://packaging.python.org/en/latest/requirements.html
-    install_requires=['numpy','scipy','pandas','matplotlib','transforms3d', 'nibabel']
-)
+setup()
diff --git a/singularity/Singularity.md b/singularity/Singularity.md
new file mode 100644
index 0000000..bfd3bc7
--- /dev/null
+++ b/singularity/Singularity.md
@@ -0,0 +1,46 @@
+# Singularity
+
+We host releases of the fsqc package as Docker images on [Dockerhub](https://hub.docker.com/r/deepmi/fsqc/tags). For use on HPC systems or in other cases where Docker is not preferred you can easily create a Singularity image from the Docker images. 
+
+## Creating s Singularity image
+For creating a singularity image from the Dockerhub just run: 
+
+```bash
+cd /home/user/my_singlarity_images
+singularity build fsqc-latest.sif docker://deepmi/fsqc:latest
+```
+
+Singularity Images are saved as `.sif` files. Here the _/homer/user/my_singlarity_images_ is the path where you want your file saved. You can change _deepmi/fsqc:latest_ with any tag provided in our [Dockerhub](https://hub.docker.com/r/deepmi/fsqc/tags).
+
+If you want to use a locally available image that you created yourself, instead run:
+
+```bash
+cd /home/user/my_singlarity_images
+singularity build fsqc-myimage.sif docker-daemon://fsqc:myimage
+```
+
+For how to create your own Docker images see our [Docker guide](../docker/Docker.md)
+
+## Using a Singularity image
+
+After building the Singularity image, you need to register at the FreeSurfer website (https://surfer.nmr.mgh.harvard.edu/registration.html) to acquire a valid license (for free). If you already have an existing FreeSurfer installation, you can use the license file of that installation as well.
+
+To run the shape and thickness analysis on a given subject using the Singularity image, execute the following command (after adjusting to your particular environment):
+
+```bash
+singularity exec \
+    -B /path/to/filename:/path_to_filename_inside_container \
+    -B /path/to/my/output/directory:/path_to_output_directory_inside_container \
+    /home/user/my_singularity_images/fsqc.sif \
+    python3 /app/fsqc/fsqc.py \
+    --filename /path_to_filename_inside_container/my_filename.mgz \
+    --outputdir /path_to_output_directory_inside_container \
+    --hemi HEMISPHERE \
+    --lut LOOKUP-TABLE \
+    --logfiledir /path_to_output_directory_inside_container
+```
+
+* The first two `-B` arguments mount your data directory and output directories into the singularity image (note that full, not relative, pathnames should be given). Inside the image, they are visible under the name following the colon (in this case `/path_to_filename_inside_container` and `/path_to_output_directory_inside_container`, but these can be different). From within the singularity image / container, there will be read and write access to the directories that are mounted into the image (unless specified otherwise).
+* The next part of the command is the name of the Singularity image, which is `fsqc.sif`. In this example it is located in `/home/user/my_singularity_images`, but the specific path will likely be different on your local system.
+* For the Singularity image, we also have to excplicitly specify the command that we want run, i.e. `python3 /app/fsqc/fsqc.py`.
+* After that, all other flags are identical to the ones that are used for the `fsqc.py` program (which are explained on the main page and the help message of the program). Note that file- and pathnames need to correspond to the targets of the file / directory mappings within the singularity image, not to the local system.


Test patch:
diff --git a/testing/data/buckner_AsegExampleNorms.csv b/testing/data/buckner_AsegExampleNorms.csv
deleted file mode 100644
index 6007608..0000000
--- a/testing/data/buckner_AsegExampleNorms.csv
+++ /dev/null
@@ -1,25 +0,0 @@
-label,lower,upper
-Left-Lateral-Ventricle,4794.075,33490.555
-Left-Inf-Lat-Vent,234.885,1816.34
-Left-Thalamus-Proper,5323.605,8124.255
-Left-Caudate,2507.355,4900.065
-Left-Putamen,3499.045,6642.255
-Left-Pallidum,1378.555,2074.71
-3rd-Ventricle,858.465,3070.25
-4th-Ventricle,1379.34,3025.855
-Brain-Stem,17243.39,24870.415
-Left-Hippocampus,2687.34,4242.315
-Left-Amygdala,932.92,1833.93
-Left-Accumbens-area,356.38,847.11
-Left-VentralDC,2901.355,4605.39
-Right-Lateral-Ventricle,4343.005,32882.06
-Right-Inf-Lat-Vent,245.275,1614.625
-Right-Thalamus-Proper,5090.55,7736.09
-Right-Caudate,2573.495,4650.905
-Right-Putamen,2787.975,6330.16
-Right-Pallidum,1212.69,2167.605
-Right-Hippocampus,2873.175,4559.015
-Right-Amygdala,1147.05,2007.23
-Right-Accumbens-area,353.775,742.955
-Right-VentralDC,2775.745,4449.59
-EstimatedTotalIntraCranialVol,1241646.3861565,1796140.634826
diff --git a/testing/testing.py b/testing/testing.py
deleted file mode 100644
index b54e20c..0000000
--- a/testing/testing.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# should be run from within the 'testing' directory, otherwise adjust pathnames; check which version is executed, i.e. an installed old one, or the modified local one?
-
-from qatoolspython import qatoolspython
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test101', subjects=['091'])
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test102', subjects=['129', '130'])
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test103')
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test104', screenshots=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test105', fornix=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test106', outlier=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test107', outlier=True, outlier_table='data/buckner_AsegExampleNorms.csv')
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test108', screenshots=True, fornix=True, outlier=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test109', subjects=['129', '130'], screenshots=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test110', subjects=['129', '130'], screenshots=True, screenshots_views=['x=10', 'x=-10', 'y=0', 'z=0'])
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test111', subjects=['129', '130'], screenshots=True, screenshots_views=['z=10', 'y=-5'], screenshots_html=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test112', subjects=['129'], screenshots=True, screenshots_base='data/129/mri/orig.mgz', screenshots_overlay='none')
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test113', subjects=['129'], screenshots=True, screenshots_overlay='data/129/mri/aparc+aseg.mgz')
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test114', subjects=['129'], screenshots=True, screenshots_overlay='none', screenshots_surf=['data/129/surf/lh.white'])
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test115', subjects=['129'], screenshots=True, screenshots_overlay='none', screenshots_surf=['data/129/surf/rh.white', 'data/129/surf/lh.pial'])
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test116', screenshots=True, fornix=True, outlier=True, fornix_html=True, shape=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test117', subjects_file='subjects.lst')
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test118', subjects=['129', '130'], screenshots=True, screenshots_views=['z=10', 'y=-5'], screenshots_layout=['2', '1'])
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test119', subjects=['129', '130'], screenshots=True, screenshots_views=['z=10', 'y=-5', 'z=-10', 'y=5', 'x=0'], screenshots_layout=['1', '5'], screenshots_html=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test120', fornix=True, fornix_html=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test121', subjects=['129', '130'], screenshots=True, screenshots_base='orig.mgz', screenshots_overlay=None, screenshots_html=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test122', subjects=['129', '130'], screenshots=True, screenshots_base='orig.mgz', screenshots_overlay='aparc+aseg.mgz', screenshots_html=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test123', subjects=['129', '130'], screenshots=True, screenshots_overlay='none', screenshots_surf="lh.white", screenshots_html=True) # note 'none' vs. None (in test121)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test124', subjects=['129', '130'], screenshots=True, screenshots_base='orig.mgz', screenshots_surf=["lh.pial", "rh.white"], screenshots_html=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test125', hypothalamus=True)
-
-qatoolspython.run_qatools(subjects_dir='data', output_dir='output/test126', hypothalamus=True, hypothalamus_html=True, fornix=True, fornix_html=True, screenshots=True, screenshots_html=True)
-
diff --git a/testing/testing.sh b/testing/testing.sh
deleted file mode 100755
index 393ebd3..0000000
--- a/testing/testing.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-# should be run from within the 'testing' directory, otherwise adjust pathnames
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test001 --subjects 091
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test002 --subjects 129 130
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test003
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test004 --screenshots
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test005 --fornix
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test006 --outlier
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test007 --outlier --outlier-table data/buckner_AsegExampleNorms.csv
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test008 --screenshots --fornix --outlier
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test009 --subjects 129 130 --screenshots
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test010 --subjects 129 130 --screenshots --screenshots_views x=10 x=-10 y=0 z=0
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test011 --subjects 129 130 --screenshots --screenshots_views z=10 y=-5 --screenshots-html
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test012 --screenshots --subjects 129 --screenshots_base data/129/mri/orig.mgz --screenshots_overlay none
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test013 --screenshots --subjects 129 --screenshots_overlay data/129/mri/aparc+aseg.mgz
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test014 --screenshots --subjects 129 --screenshots_overlay none --screenshots_surf data/129/surf/lh.white
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test015 --screenshots --subjects 129 --screenshots_overlay none --screenshots_surf data/129/surf/rh.white data/129/surf/lh.pial
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test016 --screenshots --outlier --fornix --shape --fornix-html
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test017 --subjects-file subjects.lst
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test018 --subjects 129 130 --screenshots --screenshots_views z=10 y=-5 --screenshots_layout 2 1
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test019 --subjects 129 130 --screenshots --screenshots_views z=10 y=-5 z=-10 y=5 x=0 --screenshots_layout 1 5 --screenshots-html 
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test020 --fornix --fornix-html
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test021 --screenshots --subjects 129 130 --screenshots_base orig.mgz --screenshots_overlay none --screenshots-html 
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test022 --screenshots --subjects 129 130 --screenshots_overlay aparc+aseg.mgz --screenshots-html 
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test023 --screenshots --subjects 129 130 --screenshots_overlay none --screenshots_surf lh.white --screenshots-html 
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test024 --screenshots --subjects 129 130 --screenshots_base orig.mgz --screenshots_surf lh.pial rh.white --screenshots-html 
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test025 --screenshots --hypothalamus 
-
-python3 ../qatools.py --subjects_dir data --output_dir output/test026 --screenshots --hypothalamus --hypothalamus-html --screenshots --screenshots-html --fornix --fornix-html